Reputation: 67
I have a data structure in a Dictionary which looks like this:
- device
- type
- isActive
- Data
- Manufacturer
- Build Date
- Power
...
- Example
Now if I create a For Loop it only shows me the Values of the First level which means
type, isActive, Data, Example
for (key, value) in value {
}
All below Data like Build Date or Power is Missing - how can I irritate through this levels also?
Upvotes: 1
Views: 384
Reputation: 131491
As Tomas says, this can be solved with recursion. His "walk" function simply prints the values on separate lines, but with no keys, and not formatting. Here is code that logs nested dictionaries using the structure you outlined:
//Define the keys we use in our example
enum keys: String {
case device
case type
case isActive
case Data
case Manufacturer
case BuildDate
case Power
case Example
}
//Create a sample dictionary using those keys and some random values
let dict = [keys.device.rawValue:
[keys.type.rawValue: "appliance",
keys.isActive.rawValue: true,
keys.Data.rawValue:
[keys.Manufacturer.rawValue: "GE",
keys.BuildDate.rawValue: "12/23/96",
keys.Power.rawValue: 23,
],
keys.Example.rawValue: "Foo"
]
]
//Create an extension of Dictionary for dictionaries with String Keys.
extension Dictionary where Key == String {
//Define a function to recursively build a description of our dictionary
private func descriptionOfDict(_ aDict: [String: Any], level: Int = 0) -> String {
var output = ""
var indent: String = ""
if level > 0 {
output += "\n"
for _ in 1...level {
indent += " "
}
}
for (key,value) in aDict {
output += indent + key + ": "
if let subDict = value as? [String: Any] {
//If the value for this key is another dictionary, log that recursively
output += descriptionOfDict(subDict, level: level+1)
} else {
if let aString = value as? String {
output += "\"" + aString + "\"\n"
} else {
output += String(describing: value) + "\n"
}
}
}
return output
}
//Add a description property for dictionaries
var description: String {
return descriptionOfDict(self)
}
}
print(dict.description)
That outputs:
device:
isActive: true
Data:
Manufacturer: "GE"
Power: 23
BuildDate: "12/23/96"
Example: "Foo"
type: "appliance"
The above, defining a String
property description
, changes the output when you print a [String:Any]
dictionary. If you don't want that, rename the property description
to something else like dictDescription
Upvotes: 1
Reputation: 4376
You are dealing with a Recursion problem here.
You could walk the dictionary level by level:
func walk(dictionary: [String: Any]) {
for (key, value) in dictionary {
print("\(key): \(value)")
if let value = value as? [String: Any] {
walk(dictionary: value)
}
}
}
You can change [String: Any]
type with your dictionary type (NSDictionary
, etc.)
Upvotes: 1
Reputation: 1112
Assuming you have Dictionary with type [String:Any], function to flatten it will be something like:
func flatten(_ obj:[String:Any]) -> [String:Any] {
var result = [String:Any]()
for (key, val) in obj {
if let v = val as? [String:Any] {
result = result.merging(flatten(v), uniquingKeysWith: {
$1
})
}
//I also included initial value of internal dictionary
/if you don't need initial value put next line in 'else'
result[key] = val
}
return result
}
To use that:
let d:[String:Any] = [
"test1":1,
"test2":2,
"test3":[
"test3.1":31,
"test3.2":32
]
]
let res = flatten(d)
print(res)
["test2": 2, "test3.2": 32, "test3.1": 31, "test1": 1, "test3": ["test3.2": 32, "test3.1": 31]]
note: dictionaries are not sorted structures
Upvotes: 1