I have two dictionaries in Swift with few similar values which are in dynamic mode:
dict1 = ["a1":"value 1", "b1":"value2", "c1":"value 3"]
dict2 = ["b1": "value2", "d1": "value4"]
If I want to compare these two dictionaries and want to extract only the matching keys even nested, how do I about to do that?
If you want the common keys with the value in one of them :
let intersectionDict = dict1.filter { dict2.keys.contains($0.key) }
//Or
let intersectionDict2 = dict2.filter { dict1.keys.contains($0.key) }
If you want the values to match too:
let intersectionDict3 = dict1.filter { dict2[$0.key] == $0.value }
And the result is:
print(intersectionDict3) //["b1": "value2"]
As others have shown, you can do this using a filter statement. You can make it even quicker by always filtering the smaller of the two dicts, improving the time complexity from O(dict1.size) to O(min(dict1.size, dict2.size).
extension Dictionary {
func intersectingByKeys(with other: Dictionary) -> Dictionary {
let (smallerDict, largerDict) = (self.count < other.count) ? (self, other) : (other, self)
return smallerDict.filter { key, _ in largerDict.keys.contains(key) }
}
}
let dict1 = ["a1":"value 1", "b1":"value2", "c1":"value 3"]
let dict2 = ["b1": "value2", "d1": "value4"]
print(dict1.intersectingByKeys(with: dict2))
You can create a Set from the keys of one of the dictionaries and call intersection on the Set with the keys of the other dictionary.
let matchingKeys = Set(dict1.keys).intersection(dict2.keys) // {"b1"}
Related
I'm trying to figure out the best way in Swift to add values to an Array that is a Value in a Dictionary. I want to build a dictionary of contacts sorted by the first letter of their first name. For example [A : [Aaron, Adam, etc...], B : [Brian, Brittany, ect...], ...]
I found this function:
updateValue(_:forKey:)
And tried using it in a loop:
for contact in self.contacts.sorted() {
self.contactDictionary.updateValue([contact], forKey: String(describing: contact.characters.first))
}
But when I tried to use that it replaced the existing array with a new one. I know I can manually check to see if the key in the dictionary exists, if it does, retrieve the array and then append a new value, otherwise add the new key/value pair but I'm not sure if Swift provides an easier/better way to do this.
Any insight would be much appreciated!
You can use reduce(into:) method (Swift4) and as follow:
let contacts = ["Aaron", "Adam", "Brian", "Brittany", ""]
let dictionary = contacts.reduce(into: [String:[String]]()) { result, element in
// make sure there is at least one letter in your string else return
guard let first = element.first else { return }
// create a string with that initial
let initial = String(first)
// initialize an array with one element or add another element to the existing value
result[initial] = (result[initial] ?? []) + [element]
}
print(dictionary) // ["B": ["Brian", "Brittany"], "A": ["Aaron", "Adam"]]
If you are using Swift3 or earlier you would need to create a mutable result dictionary inside the closure:
let contacts = ["Aaron", "Adam", "Brian", "Brittany", ""]
let dictionary = contacts.reduce([String:[String]]()) { result, element in
var result = result
guard let first = element.first else { return result }
let initial = String(first)
result[initial] = (result[initial] ?? []) + [element]
return result
}
print(dictionary) // ["B": ["Brian", "Brittany"], "A": ["Aaron", "Adam"]]
Note that the result is not sorted. A dictionary is an unordered collection. If you need to sort your dictionary and return an array of (key, Value) tuples you can use sorted by key as follow:
let sorted = dictionary.sorted {$0.key < $1.key}
print(sorted)
"[(key: "A", value: ["Aaron", "Adam"]), (key: "B", value: ["Brian", "Brittany"])]\n"
Swift 4's new dictionary initializers can do it all for you:
let contactInitials = contacts.filter{!$0.isEmpty}.map{ ($0.first!,[$0]) }
let dict = [Character:[String]](contactInitials, uniquingKeysWith:+)
I'm trying to figure out the best way in Swift to add values to an Array that is a Value in a Dictionary. I want to build a dictionary of contacts sorted by the first letter of their first name. For example [A : [Aaron, Adam, etc...], B : [Brian, Brittany, ect...], ...]
I found this function:
updateValue(_:forKey:)
And tried using it in a loop:
for contact in self.contacts.sorted() {
self.contactDictionary.updateValue([contact], forKey: String(describing: contact.characters.first))
}
But when I tried to use that it replaced the existing array with a new one. I know I can manually check to see if the key in the dictionary exists, if it does, retrieve the array and then append a new value, otherwise add the new key/value pair but I'm not sure if Swift provides an easier/better way to do this.
Any insight would be much appreciated!
You can use reduce(into:) method (Swift4) and as follow:
let contacts = ["Aaron", "Adam", "Brian", "Brittany", ""]
let dictionary = contacts.reduce(into: [String:[String]]()) { result, element in
// make sure there is at least one letter in your string else return
guard let first = element.first else { return }
// create a string with that initial
let initial = String(first)
// initialize an array with one element or add another element to the existing value
result[initial] = (result[initial] ?? []) + [element]
}
print(dictionary) // ["B": ["Brian", "Brittany"], "A": ["Aaron", "Adam"]]
If you are using Swift3 or earlier you would need to create a mutable result dictionary inside the closure:
let contacts = ["Aaron", "Adam", "Brian", "Brittany", ""]
let dictionary = contacts.reduce([String:[String]]()) { result, element in
var result = result
guard let first = element.first else { return result }
let initial = String(first)
result[initial] = (result[initial] ?? []) + [element]
return result
}
print(dictionary) // ["B": ["Brian", "Brittany"], "A": ["Aaron", "Adam"]]
Note that the result is not sorted. A dictionary is an unordered collection. If you need to sort your dictionary and return an array of (key, Value) tuples you can use sorted by key as follow:
let sorted = dictionary.sorted {$0.key < $1.key}
print(sorted)
"[(key: "A", value: ["Aaron", "Adam"]), (key: "B", value: ["Brian", "Brittany"])]\n"
Swift 4's new dictionary initializers can do it all for you:
let contactInitials = contacts.filter{!$0.isEmpty}.map{ ($0.first!,[$0]) }
let dict = [Character:[String]](contactInitials, uniquingKeysWith:+)
I've looked through the methods here but I can't quite find what I'm looking for. I'm new-ish to Swift. I would like to extract a subset from a Dictionary based on a Set of key values, preferably without a loop.
For example, if my key Set is of type Set<String> and I have a Dictionary of type Dictionary<String, CustomObject>, I would like to create a new Dictionary of type Dictionary<String, CustomObject> that contains only the key-value pairs associated with the keys in the Set of Strings.
I can see that I could do this with for loop, by initializing a new Dictionary<String, CustomObj>(), checking if the original Dictionary contains a value at each String in the set, and adding key-value pairs to the new Dictionary. I am wondering if there is a more efficient/elegant way to do this however.
I'd be open to finding the subset with an Array of Strings instead of a Set if there is a better way to do it with an Array of keys.
Many thanks!
Swift 5 - You can do this very simply:
let subsetDict = originalDict.filter({ mySet.contains($0.key)})
The result is a new dictionary with the same type as the original but which only contains the key-value pairs corresponding to the keys in mySet.
Your assumption is correct, there is a more concise/swift-ish way to accomplish what you need.
For example you can do it via reduce, a functional programming concept available in Swift:
let subDict = originalDict.reduce([String: CustomObject]()) {
guard mySet.contains($1.key) else { return $0 }
var d = $0
d[$1.key] = $1.value
return d
}
Or, in two steps, first filtering the valid elements, and then constructing back the dictionary with the filtered elements:
let filteredDict = originalDict.filter { mySet.contains($0.key) }
.reduce([CustomObject]()){ var d = $0; d[$1.key]=$1.value; return d }
forEach can also be used to construct the filtered dictionary:
var filteredDict = [CustomObject]()
mySet.forEach { filteredDict[$0] = originalDict[$0] }
, however the result would be good it it would be immutable:
let filteredDict: [String:CustomObject] = {
var result = [String:CustomObject]()
mySet.forEach { filteredDict2[$0] = originalDict[$0] }
return result
}()
Dummy type:
struct CustomObject {
let foo: Int
init(_ foo: Int) { self.foo = foo }
}
In case you'd like to mutate the original dictionary (instead of creating a new one) in an "intersect" manner, based on a given set of keys:
let keySet = Set(["foo", "baz"])
var dict = ["foo": CustomObject(1), "bar": CustomObject(2),
"baz": CustomObject(3), "bax": CustomObject(4)]
Set(dict.keys).subtracting(keySet).forEach { dict.removeValue(forKey: $0) }
print(dict) // ["foo": CustomObject(foo: 1), "baz": CustomObject(foo: 3)]
I know that this topic has been already discussed but I can't solve looking other answers, so sorry in advance for my ripetion!
I need to sort this Dictionary by keys
codeValueDict = ["us": "$", "it": "€", "fr": "€"]
so I need a dictionary like this
sortedDict = ["fr": "€", "it": "€", "us": "$"]
but I can't do that.
I tried this
let sortedKeysAndValues = sorted(dictionary) { $0.0 < $1.0 }
but after I need to create two arrays from this dictionary (keys and values) and, using that solution
codesArray = sortedKeysAndValues.keys.array
give me the error '[(String, String)]' does not have a member named 'keys' because that solution doesn't return exactly a dictionary.
So i tried another solution:
let prova = codiceNomeDict as NSDictionary
for (k,v) in (Array(codiceNomeDict).sorted {$0.1 < $1.1}) {
let value = "[\"\(k)\": \"\(v)\"]"
println(value)
}
Which works good but then I don't know how create a new dictionary of values.
What's the best solution? How to make it works?
The output of sorted function above is an Array. So you cannot get keys & values like a Dictionary. But you can use map function to retrieve those sorted keys & values
Return an Array containing the sorted elements of source{according}.
The sorting algorithm is not stable (can change the relative order of
elements for which isOrderedBefore does not establish an order).
let codeValueDict = ["us": "$", "it": "€", "fr": "€"]
let sortedArray = sorted(codeValueDict, {$0.0 < $1.0})
print(sortedArray)
let keys = sortedArray.map {return $0.0 }
print(keys)
let values = sortedArray.map {return $0.1 }
print(values)
Dictionaries are not ordered. If you want to enumerate over them in order, you can do that using #HoaParis's solution (which is my preference), or also
for (k,v) in sorted(codiceNomeDict, {$0.1 < $1.1}) { ... }
which is just a little better way than what you were doing before because it doesn't generate a temporary array.
But if you really want "a collection that maps one value to another and is ordered by its key" then you need to create some other data structure for that. So let's do that. It's a good learning experience.
This version just implements SequenceType and provides a get/set subscript, which is most of what you'd generally want. Making it a full CollectionType is a bit of a pain I think, since startIndex and endIndex hae to be O(1). Possible; just more than I want to do this morning.
Note the major addition of Key: Comparable. That's why Dictionary can't be ordered. There's no promise that you can sort their keys. By adding that requirement, we can.
struct SortedDictionary<Key: Hashable, Value where Key: Comparable>: SequenceType {
private var dict: Dictionary<Key, Value>
init(_ dict: Dictionary<Key, Value>) {
self.dict = dict
}
func generate() -> GeneratorOf<(Key, Value)> {
let values = Array(zip(self.dict.keys, self.dict.values))
.sorted {$0.0 < $1.0 }
return GeneratorOf(values.generate())
}
subscript(key: Key) -> Value? {
get { return self.dict[key] }
set(value) { self.dict[key] = value }
}
}
var codeValueDict = ["us": "$", "it": "€", "fr": "€"]
var sortedDict = SortedDictionary(codeValueDict)
for (k, v) in sortedDict {
println("\(k) => \(v)")
}
sortedDict["us"]
sortedDict["ab"] = "!"
sortedDict
Why would you bother with SortedDictionary when you already have sorted()? Well, usually I wouldn't. But it does offer opportunities for abstraction. You could control sort order at object creation rather than at object enumeration. You could potentially cache the sort order (though I suspect in most cases that will hurt rather than help).
But I recommend just using sorted in general.
Swift doesn't include a sorted dictionary type, and dictionaries cannot be sorted. You could add an extension that offers sorting to [(Key, Value)] by doing this:
extension Dictionary {
func sort(isOrderedBefore: (Key, Key) -> Bool) -> [(Key, Value)] {
var result: [(Key, Value)] = []
let sortedKeys = keys.array.sorted(isOrderedBefore)
for key in sortedKeys {
result.append(key, self[key]!)
}
return result
}
}
Sorting your keys by the dictionary's value is actually simpler than it appears at first:
let yourDict = ["One": "X", "Two": "B", "Three": "Z", "Four": "A"]
let sortedKeys = yourDict.keys.sort({ (firstKey, secondKey) -> Bool in
return yourDict[firstKey] < yourDict[secondKey]
})
And that's it! There's really nothing more to it.
You can't sort dictionary in such easy way. I think dictionary is using some sort of tree data structure. But after sorting you get an array of tuples. So you can get keys in such way:
let codeValueDict = ["us": "$", "it": "€", "fr": "€"]
let sortedKeysAndValues = sorted(codeValueDict) { $0.0 < $1.0 }
let keys = sortedKeysAndValues.map {$0.0 }
let values = sortedKeysAndValues.map {$0.1 }
Sort keys case-insensitive in Swift 2
Here is a function which returns a case-insensitive sorted array of keys (or any String values).
Please keep in mind that Swift’s dictionary data structure can not be stored sorted by keys in memory. So yes, you can sort it by keys but if you print it for example then the key order is again random.
/// returns an array of values sorted by values case-insensitive
func sortCaseInsensitive(values:[String]) -> [String]{
let sortedValues = values.sort({ (value1, value2) -> Bool in
if (value1.lowercaseString < value2.lowercaseString) {
return true
} else {
return false
}
})
return sortedValues
}
Call with
let dict = ["world": "Hello!", "foo": "bar", "zYeah": "a", "akey": "xval"]
let sortedKeys = sortCaseInsensitive(Array(dict.keys))
I know that this topic has been already discussed but I can't solve looking other answers, so sorry in advance for my ripetion!
I need to sort this Dictionary by keys
codeValueDict = ["us": "$", "it": "€", "fr": "€"]
so I need a dictionary like this
sortedDict = ["fr": "€", "it": "€", "us": "$"]
but I can't do that.
I tried this
let sortedKeysAndValues = sorted(dictionary) { $0.0 < $1.0 }
but after I need to create two arrays from this dictionary (keys and values) and, using that solution
codesArray = sortedKeysAndValues.keys.array
give me the error '[(String, String)]' does not have a member named 'keys' because that solution doesn't return exactly a dictionary.
So i tried another solution:
let prova = codiceNomeDict as NSDictionary
for (k,v) in (Array(codiceNomeDict).sorted {$0.1 < $1.1}) {
let value = "[\"\(k)\": \"\(v)\"]"
println(value)
}
Which works good but then I don't know how create a new dictionary of values.
What's the best solution? How to make it works?
The output of sorted function above is an Array. So you cannot get keys & values like a Dictionary. But you can use map function to retrieve those sorted keys & values
Return an Array containing the sorted elements of source{according}.
The sorting algorithm is not stable (can change the relative order of
elements for which isOrderedBefore does not establish an order).
let codeValueDict = ["us": "$", "it": "€", "fr": "€"]
let sortedArray = sorted(codeValueDict, {$0.0 < $1.0})
print(sortedArray)
let keys = sortedArray.map {return $0.0 }
print(keys)
let values = sortedArray.map {return $0.1 }
print(values)
Dictionaries are not ordered. If you want to enumerate over them in order, you can do that using #HoaParis's solution (which is my preference), or also
for (k,v) in sorted(codiceNomeDict, {$0.1 < $1.1}) { ... }
which is just a little better way than what you were doing before because it doesn't generate a temporary array.
But if you really want "a collection that maps one value to another and is ordered by its key" then you need to create some other data structure for that. So let's do that. It's a good learning experience.
This version just implements SequenceType and provides a get/set subscript, which is most of what you'd generally want. Making it a full CollectionType is a bit of a pain I think, since startIndex and endIndex hae to be O(1). Possible; just more than I want to do this morning.
Note the major addition of Key: Comparable. That's why Dictionary can't be ordered. There's no promise that you can sort their keys. By adding that requirement, we can.
struct SortedDictionary<Key: Hashable, Value where Key: Comparable>: SequenceType {
private var dict: Dictionary<Key, Value>
init(_ dict: Dictionary<Key, Value>) {
self.dict = dict
}
func generate() -> GeneratorOf<(Key, Value)> {
let values = Array(zip(self.dict.keys, self.dict.values))
.sorted {$0.0 < $1.0 }
return GeneratorOf(values.generate())
}
subscript(key: Key) -> Value? {
get { return self.dict[key] }
set(value) { self.dict[key] = value }
}
}
var codeValueDict = ["us": "$", "it": "€", "fr": "€"]
var sortedDict = SortedDictionary(codeValueDict)
for (k, v) in sortedDict {
println("\(k) => \(v)")
}
sortedDict["us"]
sortedDict["ab"] = "!"
sortedDict
Why would you bother with SortedDictionary when you already have sorted()? Well, usually I wouldn't. But it does offer opportunities for abstraction. You could control sort order at object creation rather than at object enumeration. You could potentially cache the sort order (though I suspect in most cases that will hurt rather than help).
But I recommend just using sorted in general.
Swift doesn't include a sorted dictionary type, and dictionaries cannot be sorted. You could add an extension that offers sorting to [(Key, Value)] by doing this:
extension Dictionary {
func sort(isOrderedBefore: (Key, Key) -> Bool) -> [(Key, Value)] {
var result: [(Key, Value)] = []
let sortedKeys = keys.array.sorted(isOrderedBefore)
for key in sortedKeys {
result.append(key, self[key]!)
}
return result
}
}
Sorting your keys by the dictionary's value is actually simpler than it appears at first:
let yourDict = ["One": "X", "Two": "B", "Three": "Z", "Four": "A"]
let sortedKeys = yourDict.keys.sort({ (firstKey, secondKey) -> Bool in
return yourDict[firstKey] < yourDict[secondKey]
})
And that's it! There's really nothing more to it.
You can't sort dictionary in such easy way. I think dictionary is using some sort of tree data structure. But after sorting you get an array of tuples. So you can get keys in such way:
let codeValueDict = ["us": "$", "it": "€", "fr": "€"]
let sortedKeysAndValues = sorted(codeValueDict) { $0.0 < $1.0 }
let keys = sortedKeysAndValues.map {$0.0 }
let values = sortedKeysAndValues.map {$0.1 }
Sort keys case-insensitive in Swift 2
Here is a function which returns a case-insensitive sorted array of keys (or any String values).
Please keep in mind that Swift’s dictionary data structure can not be stored sorted by keys in memory. So yes, you can sort it by keys but if you print it for example then the key order is again random.
/// returns an array of values sorted by values case-insensitive
func sortCaseInsensitive(values:[String]) -> [String]{
let sortedValues = values.sort({ (value1, value2) -> Bool in
if (value1.lowercaseString < value2.lowercaseString) {
return true
} else {
return false
}
})
return sortedValues
}
Call with
let dict = ["world": "Hello!", "foo": "bar", "zYeah": "a", "akey": "xval"]
let sortedKeys = sortCaseInsensitive(Array(dict.keys))