Swift 4: Find value in nested, dynamic Dictionary<String, Any> recursively - swift

In a given Dictionary, I need to find a nested Dictionary ([String : Any]) for a given key.
The general structure of the Dictionary (e.g. nesting levels, value types) is unknown and given dynamically. [1]
Inside of this sub-Dictionary, there is a given value for the key "value" (don't ask) which needs to be fetched.
Here's an example:
let theDictionary: [String : Any] =
[ "rootKey" :
[ "child1Key" : "child1Value",
"child2Key" : "child2Value",
"child3Key" :
[ "child3SubChild1Key" : "child3SubChild1Value",
"child3SubChild2Key" :
[ "comment" : "child3SubChild2Comment",
"value" : "child3SubChild2Value" ]
],
"child4Key" :
[ "child4SubChild1Key" : "child4SubChild1Value",
"child4SubChild2Key" : "child4SubChild2Value",
"child4SubChild3Key" :
[ "child4SubChild3SubChild1Key" :
[ "value" : "child4SubChild3SubChild1Value",
"comment" : "child4SubChild3SubChild1Comment" ]
]
]
]
]
With brute force and pseudo memoization, I managed to hack a function together that iterates through the entire Dictionary and fetches the value for a given key:
func dictionaryFind(_ needle: String, searchDictionary: Dictionary<String, Any>) -> String? {
var theNeedleDictionary = Dictionary<String, Any>()
func recurseDictionary(_ needle: String, theDictionary: Dictionary<String, Any>) -> Dictionary<String, Any> {
var returnValue = Dictionary<String, Any>()
for (key, value) in theDictionary {
if value is Dictionary<String, Any> {
if key == needle {
returnValue = value as! Dictionary<String, Any>
theNeedleDictionary = returnValue
break
} else {
returnValue = recurseDictionary(needle, theDictionary: value as! Dictionary<String, Any>)
}
}
}
return returnValue
}
// Result not used
_ = recurseDictionary(needle, theDictionary: searchDictionary)
if let value = theNeedleDictionary["value"] as? String {
return value
}
return nil
}
This works so far. (For your playground testing pleasure:
let theResult1 = dictionaryFind("child3SubChild2Key", searchDictionary: theDictionary)
print("And the result for child3SubChild2Key is: \(String(describing: theResult1!))")
let theResult2 = dictionaryFind("child4SubChild3SubChild1Key", searchDictionary: theDictionary)
print("And the result for child4SubChild3SubChild1Key is: \(String(describing: theResult2!))")
let theResult3 = dictionaryFind("child4Key", searchDictionary: theDictionary)
print("And the result for child4Key is: \(String(describing: theResult3))")
).
My question here:
What would be a more clean, concise, "swifty", way to iterate through the Dictionary and - especially - break completely out of the routine as soon the needed key has been found?
Could a solution even be achieved using a Dictionary extension?
Thanks all!
[1] A KeyPath as described in Remove nested key from dictionary therefor isn't feasible.

A more compact recursive solution might be:
func search(key:String, in dict:[String:Any], completion:((Any) -> ())) {
if let foundValue = dict[key] {
completion(foundValue)
} else {
dict.values.enumerated().forEach {
if let innerDict = $0.element as? [String:Any] {
search(key: key, in: innerDict, completion: completion)
}
}
}
}
the usage is:
search(key: "child3SubChild2Key", in: theDictionary, completion: { print($0) })
which gives:
["comment": "child3SubChild2Comment", "value": "child3SubChild2Subchild1Value"]
alternatively, if you don't want to use closures, you might use the following:
extension Dictionary {
func search(key:String, in dict:[String:Any] = [:]) -> Any? {
guard var currDict = self as? [String : Any] else { return nil }
currDict = !dict.isEmpty ? dict : currDict
if let foundValue = currDict[key] {
return foundValue
} else {
for val in currDict.values {
if let innerDict = val as? [String:Any], let result = search(key: key, in: innerDict) {
return result
}
}
return nil
}
}
}
usage is:
let result = theDictionary.search(key: "child4SubChild3SubChild1Key")
print(result) // ["comment": "child4SubChild3SubChild1Comment", "value": "child4SubChild3SubChild1Value"]

The following extension can be used for finding values of a key in nested dictionaries, where different levels each can contain the same key associated with a different value.
extension Dictionary where Key==String {
func find<T>(_ key: String) -> [T] {
var keys: [T] = []
if let value = self[key] as? T {
keys.append(value)
}
self.values.compactMap({ $0 as? [String:Any] }).forEach({
keys.append(contentsOf: $0.find(key))
})
return keys
}
}

Related

How to remove all Int values from Dictionary?

I have a dictionary:
var dictionary: [String: Any] = ["test1": "one", "test2": "two", "test3": 3, "test4": 4]
What is the best way to get a new dictionary, but only with String values?
You can write an extension for Dictionary:
extension Dictionary where Key == String, Value: Any {
var withoutInt: [String: Any] {
return self.filter{ $0.value is String }
}
}
And then use it:
dictionary = dictionary.withoutInt
How about this:
for (key, value) in dictionary {
if let value = value as? Int {
dictionary.removeValue(forKey: key)
}
}
Pseudo steps:
1.) Check each key/value pair in the dictionary
2.) Check if the value is an Integer
3.) If value is an integer, remove dictionary value associated with that key for that pair
Caveat: This is modifying your original dictionary. If you wanted to create a new (second instance) of your dictionary and keep the original intact, I think this would be as simple as creating a new dictionary and assigning it to the values of the original, then modifying the second dictionary instead.
You can just filter those that are numbers.
Get those that are strings.
dictionary = dictionary.filter({ ($0.value as? Int) == nil })
Get those that are Ints
dictionary = dictionary.filter({ ($0.value as? Int) != nil })
All we do here are filter by cast typing to Int and seeing if it is nil or not.
Note: Could just do to original dictionary --
var dictionary: [String: Any] = ["test1": "one", "test2": "two", "test3": 3, "test4": 4].filter({ ($0.value as? Int) == nil })
OR
var dictionary: [String: Any] = ["test1": "one", "test2": "two", "test3": 3, "test4": 4].filter({ ($0.value as? Int) != nil })
for (key, value) in dictionary {
if let v = value as? Int {
dictionary.removeValue(forKey : key)
}
}
if you don't modify parent dictionary then
func removeInt(dictionary : [String : Any]) ->[String : Any] {
var dict2 = NSMutableDictionary()
for (key, value) in dictionary {
if let v = value as? Int {
dict2.setValue(v, forKey : key)
}
}
return (dict2 as! [String : Any])
}

Recursively change some keys in a Dictionary in Swift

I have a dictionary
var dictionary: Any = ["det" : ["val" : "some","result" : ["key1" : "val1","key2" : "val2"],"key3" :["val1", "val2"]]]
and a mapping function below
func getMappedKey(_ key: String) -> String? {
var mapping: Dictionary = [
"key1" : "key3",
"key2" : "key4",
"det" : "data"
]
return mapping[key]
}
Now I want to change some keys in the same dictionary using the mapping function above. So after the change, the dictionary should look like
["data" : ["val" : "some","result" : ["key3" : "val1","key4" : "val2"],"key3" :["val1", "val2"]]]
So for that I wrote a function below
func translatePayload(_ payload: inout Any) {
if let _ = payload as? String {
return
} else if var payload = payload as? Dictionary<String, Any> {
for (key, value) in payload {
if let newKey = getMappedKey(key) {
if let _ = payload.removeValue(forKey: key) {
payload[newKey] = value
}
}
var nextPayload = value
translatePayload(&nextPayload)
}
} else if let payload = payload as? Array<Any> {
for value in payload {
var nextPayload = value
translatePayload(&nextPayload)
}
}
}
and when I call the function
translatePayload(&dictionary)
print(dictionary)
it does not change the keys in the same dictionary. Can someone please point out what is wrong with this code. Thanks in advance
Your code is perfectly fine, you just updated the local variable instead of the parametric one because you used the same name. Just change the local variable payload to dictionary and array or anything else you like.
Here is the final code:
func translatePayload(_ payload: inout Any) {
if let _ = payload as? String {
return
} else if var dictionary = payload as? Dictionary<String, Any> { // Here dictionary instead of payload
for (key, value) in dictionary {
var nextPayload = value
translatePayload(&nextPayload)
if let newKey = getMappedKey(key) {
if let _ = dictionary.removeValue(forKey: key) {
dictionary[newKey] = nextPayload
}
} else {
dictionary[key] = nextPayload
}
}
payload = dictionary
} else if let array = payload as? Array<Any> { // Here array instead of payload
var updatedArray = array
for (index, value) in array.enumerated() {
var nextPayload = value
translatePayload(&nextPayload)
updatedArray[index] = nextPayload
}
payload = updatedArray // Assign the new changes
}
}
translatePayload(&dictionary)
print(dictionary)
Not really a direct answer to the question "what's wrong", but I'd go with something like:
let dictionary = ["det" : ["val" : "some","result" : ["key1" : "val1", "key2" : "val2"],"key3" :["val1", "val2"]]]
func getMapped(key: String) -> String {
var mapping: Dictionary = [
"key1" : "key3",
"key2" : "key4",
"det" : "data"
]
return mapping[key] ?? key
}
func translate(payload:Any, map:(String)->String) -> Any {
switch payload {
case let value as String:
return value
case let value as [String:Any]:
return value.reduce(into:[String:Any]()) {
$0[map($1.0)] = translate(payload: $1.1, map:map)
}
case let value as [Any]:
return value.map { translate(payload: $0, map:map) }
default:
fatalError("Unknown data type")
}
}
let output = translate(payload: dictionary, map:getMapped(key:))
To really take advantage of the functional spirit of Swift.

Create a Dictionary from optionals

I some optionals: numberOfApples:Int?, numberOfBananas:Int?, numberOfOlives:Int? and I'd like to create a dictionary of just the set values. Is there way do succinctly create this?
The closest I've got is:
// These variables are hard-coded for the example's
// sake. Assume they're not known until runtime.
let numberOfApples: Int? = 2
let numberOfBananas: Int? = nil
let numberOfOlives: Int? = 5
let dict: [String:Int?] = ["Apples" : numberOfApples,
"Bananas" : numberOfBananas,
"Olives" : numberOfOlives]
And I'd like to dict to be of type: [String:Int] like so:
["Apples" : 2,
"Olives" : 5]
But this gives me a dictionary of optionals and accessing a value by subscripting gives my a double-wrapped-optional.
I realise that I could do this with a for-loop, but I was wondering if there's something more elegant.
Many thanks in advance.
Personally I would do it this way (and it's how I personally do this when it comes up):
var dict: [String: Int] = [:]
dict["Apples"] = numberOfApples
dict["Bananas"] = numberOfBananas
dict["Olives"] = numberOfOlives
Simple. Clear. No tricks.
But if you wanted to, you could write Dictionary.flatMapValues (to continue the pattern of Dictionary.mapValues). It's not hard. (EDIT: Added flattenValues() to more closely match original question.)
extension Dictionary {
func flatMapValues<T>(_ transform: (Value) throws -> T?) rethrows -> [Key: T] {
var result: [Key: T] = [:]
for (key, value) in self {
if let transformed = try transform(value) {
result[key] = transformed
}
}
return result
}
func flattenValues<U>() -> [Key: U] where Value == U? {
return flatMapValues { $0 }
}
}
With that, you could do it this way, and that would be fine:
let dict = [
"Apples" : numberOfApples,
"Bananas": numberOfBananas,
"Olives" : numberOfOlives
].flattenValues()
You can use filter and mapValues. You first filter all pairs where the value is not nil and then you can safely force unwrap the value. This will change the dict type to [String: Int].
let dict = [
"Apples": numberOfApples,
"Bananas": numberOfBananas,
"Olives": numberOfOlives
]
.filter({ $0.value != nil })
.mapValues({ $0! })
print(dict) //["Olives": 5, "Apples": 2]
Try this:
let numberOfApples: Int? = 5
let numberOfBananas: Int? = nil
let numberOfOlives: Int? = 5
let dict: [String: Int?] = [
"Apples": numberOfApples,
"Bananas": numberOfBananas,
"Olives": numberOfOlives
]
extension Dictionary {
func flatMapValues<U>() -> [Key: U] where Value == Optional<U> {
return reduce(into: [:]) { $0[$1.key] = $1.value }
// Keeping this line as it provides context for comments to this answer. You should delete it if you copy paste this.
// return filter { $0.value != nil } as! [Key : U]
}
}
let unwrappedDict = dict.flatMapValues()
let foo: Int?? = dict["Apples"]
let bar: Int? = unwrappedDict["Apples"]

Is there a way to use guard statements more concisely?

I'm using Gloss for my JSON instantiation. Here is a sample class:
public class MyObj: Decodable
{
let id_user : String
let contact_addr1 : String
let contact_addr2 : String?
let contact_city : String
let contact_state : String
let contact_zip : String
let points : Int
// Deserialization
required public init?(json: JSON)
{
guard let id_user : String = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
guard let contact_addr1 : String = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
guard let contact_city : String = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
guard let contact_state : String = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
guard let contact_zip : String = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
guard let points : Int = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
self.id_user = id_user
self.contact_addr1 = contact_addr1
self.contact_addr2 = "somekey" <~~ json
self.contact_city = contact_city
self.contact_state = contact_state
self.contact_zip = contact_zip
self.contact_points = points
}
}
I have a lot of model classes. Hundreds of members between them. Writing a multi-line guard statement for each one really junks up my code. Is there any way I can encapsulate the guard functionality into something more concise? Maybe a function or something like:
shortGuard("memberName", "jsonKey")
Maybe there is a way to guard against an array of string keys?
There are a huge variety of ways to accomplish this. They all boil down to writing a wrapper function to map your keys to values. Here are a couple quick examples I thought of, but as I say there are many ways to do this depending on what you're after:
enum JSONError: Error {
case keyNotFound(String)
}
extension JSON {
func values<T>(for keys: [String]) throws -> [T] {
var values = [T]()
for key in keys {
guard let value: T = key <~~ self else {
throw JSONError.keyNotFound(key)
}
values.append(value)
}
return values
}
func values<T>(for keys: [String], closure: ((_ key: String, _ value: T) -> Void)) throws {
for key in keys {
guard let value: T = key <~~ self else {
throw JSONError.keyNotFound(key)
}
closure(key, value)
}
}
}
The first validates all keys before you can use any of them and will throw if one isn't present. You'd use it like so:
do {
let keys = ["foo", "bar"]
// The type of the values constant is important.
// In this example we're saying look for values of type Int.
let values: [Int] = try json.values(for: keys)
for (index, key) in keys.enumerated() {
print("value for \(key): \(values[index])")
}
} catch JSONError.keyNotFound(let key) {
assertionFailure("key not found \(key)")
}
The second one will pass back key, value pairs to a closure as they appear in your keys array and will throw at the first one it finds that doesn't exist.
do {
let keys = ["foo", "bar"]
// The type of the closure's value argument is important.
// In this example we're saying look for values of type String.
try json.values(for: keys) { (key, value: String) in
print("value for key \(key) is \(value)")
}
} catch JSONError.keyNotFound(let key) {
assertionFailure("key not found \(key)")
}
Using the first version in an init?() function for your class, we have something like this:
public struct MyObj: Decodable {
public let id_user : String
public let contact_addr1 : String
public let contact_addr2 : String?
public let points : Int
public init?(json: S) {
do {
let stringKeys = ["id_user", "contact_addr1"]
let stringValues: [String] = try json.values(for: stringKeys)
id_user = stringValues[0]
contact_addr1 = stringValues[1]
// this isn't required, so just extract with no error if it fails
contact_addr2 = "contact_addr2" <~~ json
let intKeys = ["points"]
let intValues: [Int] = try json.values(for: intKeys)
points = intValues[0]
} catch JSONError.keyNotFound(let key) {
assertionFailure("key \(key) not found in JSON")
return nil
} catch {
return nil
}
}
}
I have not used Gloss, and it mostly seems to be unnecessary considering that it is simple enough to parse JSON safely without needing an extra library, or using unfamiliar syntax.
Option 1:
You can group the optional unwrapping in a single guard statement.
Example:
public struct MyObj {
let id_user : String
let contact_addr1 : String
let contact_addr2 : String?
let points : Int
public init?(json: Any) {
guard
let entities = json as? [String : Any],
let id_user = entities["some key"] as? String,
let contact_addr1 = entities["some key"] as? String,
let points = entities["some key"] as? Int
else {
assertionFailure("...")
return nil
}
self.id_user = id_user
self.contact_addr1 = contact_addr1
self.contact_addr2 = entities["some key"] as? String
self.contact_points = points
}
}
Option 2:
Another approach would be to eliminate the guard statements altogether, and let the parser throw an error during parsing, and use an optional try to convert the result to nil.
Example:
// Helper object for parsing values from a dictionary.
// A similar pattern could be used for arrays. i.e. array.stringAt(10)
struct JSONDictionary {
let values: [String : Any]
init(_ json: Any) throws {
guard let values = json as? [String : Any] else {
throw MyError.expectedDictionary
}
self.values = values
}
func string(_ key: String) throws -> String {
guard let value = values[key] as? String else {
throw MyError.expectedString(key)
}
return value
}
func integer(_ key: String) throws -> Int {
guard let value = values[key] as? Int else {
throw MyError.expectedInteger(key)
}
return value
}
}
Parser:
public struct MyObj {
let id_user : String
let contact_addr1 : String
let contact_addr2 : String?
let points : Int
public init(json: Any) throws {
// Instantiate the helper object.
// Ideally the JSONDictionary would be passed by the caller.
let dictionary = try JSONDictionary(json),
self.id_user = try dictionary.string("some key"),
self.contact_addr1 = try dictionary.string("some key"),
self.points = try dictionary.integer("some key")
// Results in an optional if the string call throws an exception
self.contact_addr2 = try? dictionary.string("some key")
}
}
Usage:
// Instantiate MyObj from myJSON.
// myObject will be nil if parsing fails.
let myObject = try? MyObj(json: myJSON)

Dictionary of dictionaries in Swift

I'd like to build a dictionary of dictionaries. In Swift how do I declare a dictionary with a key of a String and the value of a dictionary of this same type? I need to be able to have potentially infinite nests. (Kind of like building a tree using nodes. Except it's not a tree, it's a dictionary.)
I tried using AnyObject, but get a conversion error:
var node1: Dictionary<String, AnyObject?> = ["foo" : nil]
var node2: Dictionary<String, AnyObject?> = ["bar" : node1] // ERROR: Cannot convert value of type 'Dictionary<String, AnyObject?>' (aka 'Dictionary<String, Optional<AnyObject>>') to expected dictionary value type 'Optional<AnyObject>'
Is there a type-safe way of doing this (i.e., not using AnyObject?)
You can achieve something like this with a nice API and type safety in swift by using a struct and an enumeration.
enum RecursiveDictValue<KeyType: Hashable, ValueType> {
case Value(ValueType)
case Dict(RecursiveDict<KeyType, ValueType>)
}
struct RecursiveDict<KeyType: Hashable, ValueType> {
typealias OwnType = RecursiveDict<KeyType, ValueType>
private var dict: [KeyType: RecursiveDictValue<KeyType, ValueType>]
init() {
dict = [:]
}
init(dict: [KeyType: RecursiveDictValue<KeyType, ValueType>]) {
self.dict = dict
}
// this ensures that we can safely chain subscripts
subscript(key: KeyType) -> OwnType {
get {
switch dict[key] {
case let .Dict(dict)?:
return dict
default:
return RecursiveDict<KeyType, ValueType>()
}
}
set(newValue) {
dict[key] = .Dict(newValue)
}
}
subscript(key: KeyType) -> ValueType? {
get {
switch dict[key] {
case let .Value(value)?:
return value
default:
return nil
}
}
set(newValue) {
if let newValue = newValue {
dict[key] = RecursiveDictValue<KeyType, ValueType>.Value(newValue)
} else {
dict[key] = nil
}
}
}
}
This works quite nicely (note that you need to help swift with the types though):
var dict = RecursiveDict<String, Int>(dict: ["value":.Value(1),
"dict":.Dict(RecursiveDict<String, Int>(dict: ["nestedValue": .Value(2)]))])
if let value: Int = dict["value"] {
print(value) // prints 1
}
if let value: Int = dict["dict"]["nestedValue"] {
print(value) // prints 2
}
It also fails as intended when you do stuff that can't work.
if let value: Int = dict["dict"] {
print(value) // is not executed
}
if let value: Int = dict["dict"]["nestedDict"]["nestedValue"] {
print(value) // is not executed
}
And you can even set values in nested dictionaries that haven't been created yet!:
dict["dict"]["nestedDict2"]["nestedValue"] = 3
if let value: Int = dict["dict"]["nestedDict2"]["nestedValue"] {
print(value) // prints 3
}
I was working with firebase, and i needed to achieve an structure similar to this:
["llave3": ["hola": "", "dos": ""], "llave1": ["hola": "", "dos": ""], "llave2": ["hola": "", "dos": ""]]
This is a nested dictionary, or a dictionary of dictionaries. I achieve this by simply doing this:
var array = ["llave1", "llave2","llave3"]
var dictOfDictionarys = [String : [String : String]] ()
for items in array {
dictOfDictionarys[items] = ["hola":"","dos":""]
}
Very less info.. and you mean infinite nested dictionaries? I dont think so..
func returnDict() -> Dictionary<String, AnyObject> {
return ["Got You" : returnDict()]
}
var x : Dictionary<String, AnyObject> = ["GotYou" : returnDict()]
Just saying, nothing better can happen to this other than a crash
this is a case of infinite recursion. When you have infinite dictionaries, it doesnt mean that it is going to run forever. It means that it is going to run till your device runs out of memory. A call to function returnDict, calls returnDict, which again calls returnDict and so on.
A recursion is basically adding a method onto the stack of pre-existing stack of methods in memory.. this can happen until the stack overflows. Hence, stackOverFlow
let node1: Dictionary<String, AnyObject!> = ["foo" : nil]
var node2 = ["bar" : node1]
Playground approves of it
The reason is that like in Objective-C values in the Dictionary type must be non-optional.
It's not very useful anyway because in Swift assigning a nil value to a key removes the key.
var node1: Dictionary<String, AnyObject> = ["foo" : "Hello"]
var node2: Dictionary<String, AnyObject> = ["bar" : node1]