Access to key inside Dictionary get in Swift - ios

Is it possible to access the key inside of a Dictionary get in Swift ?
The main idea is:
In this code
var _dict:[String:String] = [:]
var dict:[String:String] {
//get the key
return _dict
}
_dict = ["key 1":"Value 1","key 2":"Value 2"]
print(dict["key 1"])
Access the key to check if the value exists, if it exists return the value if not generate the value for that key

Did you know that Dictionary allows you to specify a default value in its subscript to avoid dealing with optional values. It works like so:
let dict = ["a": 1, "b": 2]
let c = dict["c", default: 3]
print(c) // 3
but that doesn't change the dictionary - it's still only has "a" and "b" keys, which is the expected behavior.
I think what you're asking about is whether it's possible to mutate the dictionary with a default value. The answer is yes - you could create a subscript with a mutating get.
But it's the wrong thing to do!
You will effectively have a getter with side-effects, which is typically a bad practice.
In any case, this is how you could implement a subscript with a new parameter setDefault:
extension Dictionary {
subscript(key: Key, setDefault defaultVal: #autoclosure () -> Value) -> Value {
mutating get {
if let val = self[key] {
return val
} else {
let val = defaultVal()
self[key] = val
return val
}
}
}
}
// dict needs to be a var now
var dict = ["a": 1, "b": 2]
let c = dict["c", setDefault: 3]
Now, this will mutate dict and it will be ["a": 1, "b": 2, "c": 3]

Related

"For-in loop requires '[String]?' to conform to 'Sequence'; did you mean to unwrap optional?" but I don't think I am using optionals

Here is what I'm trying to do but simplified down:
var adictionary = [String:[String]]()
adictionary["A"] = ["B", "C"]
adictionary["B"] = ["A", "C"]
adictionary["C"] = ["A"]
adictionary["D"] = []
var newdic = Array(adictionary.keys).reduce(into: [String: Bool]()) { $0[$1] = false } //I make an arr from a dictionary's keys of type [String:[String]]
for (key, val) in newdic{
for arr in adictionary[key]{
if !(adictionary[arr].contains(key)){
newdic[key] = true
}
}
}
I get these errors, when I run the above simplified version of the code I am trying to run in an ios app:
main.swift:12:28: error: value of optional type '[String]?' must be unwrapped to a value of type '[String]'
for arr in adictionary[key]{
main.swift:12:28: note: coalesce using '??' to provide a default when the optional value contains 'nil'
for arr in adictionary[key]{
main.swift:12:28: note: force-unwrap using '!' to abort execution if the optional value contains 'nil'
for arr in adictionary[key]{
I don't understand, what is wrong with what I am doing? What do these errors mean? It seems like Swift thinks I have an optional somewhere? But I don't see how I do... Any help is much appreciated.
If I change this line: for arr in adictionary[key]{
To: for arr in adictionary[key]!{
It fixes the issues and new errors appear on the if !(a... line. I still don't understand why that fixed it.
Swift dictionary accesses always return optional values, because the lookup will return nil if the key doesn't exist. It is up to you to handle this resulting optional is a safe way. Adding ! is rarely the right way to fix it because your code will crash if nil is returned.
The first time you get an optional is here:
for arr in adictionary[key] {
Again, Swift doesn't know if key exists in adictionary so it returns an optional. A safe way to fix this is to use the dictionary lookup which returns a default value when the key doesn't exist. In this case, returning an empty array seems like a good choice since your for loop will then just do nothing:
for arr in adictionary[key, default:[]] {
Next, you get an optional here:
if !(adictionary[arr].contains(key)){
Again, you need to decide how to safely handle the fact that dictionary[arr] could return nil. The same trick works here: return an empty array if arr doesn't exist:
if !(adictionary[arr, default:[]].contains(key)){
Here is the final version of your code:
var adictionary = [String:[String]]()
adictionary["A"] = ["B", "C"]
adictionary["B"] = ["A", "C"]
adictionary["C"] = ["A"]
adictionary["D"] = []
var newdic = Array(adictionary.keys).reduce(into: [String: Bool]()) { $0[$1] = false } //I make an arr from a dictionary's keys of type [String:[String]]
print(newdic)
for key in newdic.keys {
for arr in adictionary[key, default:[]] {
if !(adictionary[arr, default:[]].contains(key)){
newdic[key] = true
}
}
}
Here is a similar approach without having your mentioned problem.
var adictionary = [String:[String]]()
adictionary["A"] = ["B", "C"]
adictionary["B"] = ["A", "C"]
adictionary["C"] = ["A"]
adictionary["D"] = []
var newdic = Array(adictionary.keys).reduce(into: [String: Bool]()) { $0[$1] = false }
let notEmptValues = adictionary.values.filter { $0.count > 0 }.reduce([], +)
let duplicates = Array(Set(notEmptValues.filter { i in notEmptValues.filter { $0 == i }.count > 1 })).sorted(by: { $0 < $1 })
if let result = adictionary.first(where: { $0.value.sorted(by: { $0 < $1 }) == duplicates })?.key {
newdic[result] = true
}
print(newdic)

How to use Swift's higher order functions for parsing dynamic dictionary data in swift?

I am trying to parse the following json and want to retrieve the "key" of a dictionary whose value matches with the given value.
{ "OuterArrayHolder" :
[
{
"dictDynamicKey" : ["dynamicValue1", "dynamicValue2", "dynamicValue3"]
},
{
"dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]
},
]
}
[Note: Here in above json, all the keys and values are dynamic except "OuterArrayHolder".]
I have implemented it in a non-Swifty way and currently getting the expected output, but I am not getting how to accomplish the same behaviour using swift's higher-order functions.
Input : "dynamicValue2"
Expected output : "dictDynamicKey"
Current solution:
let inputValue = "dynamicValue2"
if !outerArrayHolder.isEmpty {
for dynamicDict in outerArrayHolder {
for (key, value) in dynamicDict {
if value.empty || !value.contains(inputValue) {
continue
} else {
//here if inputValue matches in contianed array (value is array in dictionary) then I want to use its "repective key" for further businisess logic.
}
}
}
}
I want to reduce these two for loops and want to use higher-order functions to achieve the exact behavior, Any help in this regard is really appreciated.
Can we convert your algorithm to a functional style? Yes. Is it a good idea? Probably not in this case. But here's how.
You didn't give any type information, so I'll use this type:
let outerArrayHolder: [[String: Any]] = [
[
"dictDynamicKey": ["dynamicValue1", "dynamicValue2", "dynamicValue3"]
],
[
"dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]
],
]
And you want to find the key corresponding to the array that contains inputValue:
let inputValue = "dynamicValue2"
The functional strategy is to map each dictionary in outerArrayHolder to its first key that has a matching value. If a dictionary has no such key, the dictionary is mapped to nil. Then we throw away the nils and take the first remaining value.
We can do it with filter, as requested:
let key = outerArrayHolder.lazy
.compactMap {
$0.lazy
.filter { ($0.value as? [String])?.contains(inputValue) ?? false }
.map { $0.key }
.first }
.first
But we can save a lazy and a first using first(where:):
let key = outerArrayHolder.lazy
.compactMap({
$0
.first(where: { ($0.value as? [String])?.contains(inputValue) ?? false })
.map { $0.key }
}).first
I don't see what this has to do with higher-order functions. If the outer key is known, I would simply write
// just building your structure
let d1 = ["dictDynamicKey" : ["dynamicValue1", "dynamicValue2", "dynamicValue3"]]
let d2 = ["dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]]
let d = ["OuterArrayHolder" : [d1, d2]]
// this is the actual code:
func find(_ target:String) -> String? {
for dict in d["OuterArrayHolder"]! {
for (k,v) in dict {
if v.contains(target) {return k}
}
}
return nil
}
That's just what you're doing, only it's clean.
There's no higher order function that does precisely what you're looking for. The closest is first(where:), but the problem is that the result is just Bool, and you don't have a way to cleanly fish out data related to the found case.
You could write something like:
extension Sequence {
func findFirst<T>(where predicate: (Element) throws -> T?) rethrows -> T? {
for element in self {
if let result = try predicate(element) {
return result
}
}
return nil
}
}
and then use it like:
let dictionaries = [
[
"dictDynamicKey" : ["dynamicValue1", "dynamicValue2", "dynamicValue3"]
],
[
"dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]
],
]
let desiredValue = "dynamicValue2"
extension Sequence {
func findFirst<T>(where predicate: (Element) throws -> T?) rethrows -> T? {
for element in self {
if let result = try predicate(element) {
return result
}
}
return nil
}
}
let result = dictionaries.findFirst(where: { dict in
dict.findFirst(where: { key, values in
values.contains(desiredValue) ? key : nil
})
})
print(result as Any) // => Optional("dictDynamicKey")
But it's probably more complexity than it's probably worth. I would recommend Matt's solution.
Scaled solution
You haven't clarified on this, but I suspect that you probably need to do this a bunch of times. In that case, linear searching through this gets really slow. By searching for keys by their values, you're not taking advantage of the key benefit of dictionaries: constant-time access to a value by its key. Your code is:
Linear-searching through the array of dictionaries, introduces an O(dictionaries.count) factor
For each dict in the array in #1, linear-searching through the key/value pairs, which introduces a O(dict.count) factor
For each key/value pair in the dict in #2, linear-searching through array of values, which introduces a O(valueArray.count) factor.
The total time complexity multiplies up to O(dictionaries.count * averageDict.count * averageValueArray.count), which gets really slow really quick.
Instead, you can spend some compute cost up-front, to create a new data structure that is better able to service the kinds of queries you want to run on it. In this case, you can "invert" a dictionary.
extension Dictionary {
func inverted<T>() -> [T: Key] where Dictionary.Value == [T] {
let invertedKeyValuePairs = self
.lazy
.flatMap { oldKey, oldValues in
oldValues.map { oldValue in (key: oldValue, value: oldKey) as (T, Key) }
}
return Dictionary<T, Key>(uniqueKeysWithValues: invertedKeyValuePairs)
}
}
// Example usage:
let valuesByKeys = [
"a": [1, 2, 3],
"b": [4, 5, 6]
]
let keysPerValue = valuesByKeys.inverted()
keysPerValue.forEach { key, value in print("key: \(key), value: \(value)") }
// Which results in:
// key: 3, value: a
// key: 4, value: b
// key: 5, value: b
// key: 1, value: a
// key: 6, value: b
// key: 2, value: a
Given such an inverted implementation, you can invert each dict of your input set, and merge them all together:
let invertedDictionary = Dictionary(uniqueKeysWithValues: dictionaries.flatMap { $0.inverted() })
invertedDictionary.forEach { key, value in print("key: \(key), value: \(value)") }
// Result:
key: dynamicValue6, value: dictAnotherDynamicKey
key: dynamicValue1, value: dictDynamicKey
key: dynamicValue2, value: dictDynamicKey
key: dynamicValue3, value: dictDynamicKey
key: dynamicValue4, value: dictAnotherDynamicKey
key: dynamicValue5, value: dictAnotherDynamicKey
You can store and share this dictionary, which can give constant time (O(1)) access to the key that was associated with any desired value:
print(invertedDictionary[desiredValue] as Any) // => Optional("dictDynamicKey")

How to unwrap NSMutableDictionary.allkeys in optional String Array

I am trying to get all the key values of NSMutableDictionary as String Array. I am using this myNSMutableDictionary.allkeys to get the values as an Array but I cannot find a way to unwrap the key values.
This is what I have tried so far:
for (key, _) in NSMutableDictionary {
println("THIS IS MY NEW KEY\(key)")
}
And I tried this
var myArray:NSArray = myNSMutableDictionary.allKeys
var string:NSString? = uniqueIDArray[0] as? NSString
println("This is unwraped value\(string!)")
And this
var myArray:Array = myNSMutableDictionary.allKeys
println("This is unwraped value\(myArray[0])")
I keep getting the value as Optional("kMSZgoTmiX") instead of kMSZgoTmiX which is the key value I need
Thank you for all your help!
So you've got a dictionary with values that are strings (and keys that are something, assume String):
var dictionaryOfStringValues : [String:String] = /* your dictionary */
And you want to iterate over the contents:
for (key, val) in dictionaryOfStringValues {
// use key and val
}
If you just want the values in a way you can easily iterate over:
var theValues = dictionaryOfStringValues.values
If you insist that theValues be an Array:
var theValuesAsAnArray = Array(dictionaryOfStringValues.values)
If you are starting with an NSMutableDictionary, then convert it at the point where it FIRST ENTERS your Swift code into a Swift Dictionary. Use an as variant to do that. After that, pure Swift.
Like this:
7> for (key, value) in ["a":1, "b":2] {
8. println (key)
9. println (value)
10. }
b
2
a
1
let myNSMutableDictionary = NSMutableDictionary()
myNSMutableDictionary["myKey1"] = 5
myNSMutableDictionary["myKey2"] = 10
myNSMutableDictionary["myKey3"] = 15
let myKeysArrayUnsorted = myNSMutableDictionary.allKeys as [String]
let myValuesArrayUnsorted = myNSMutableDictionary.allValues as [Int]
let keyString = myKeysArrayUnsorted[0] // "myKey2"
let keyValue = myNSMutableDictionary[keyString] as Int // 10
println("This is my first unsorted key \(keyString) = \(keyValue)")
let myKeysArraySorted = (myNSMutableDictionary.allKeys as [String]).sorted(<)
for key in myKeysArraySorted {
println(myNSMutableDictionary[key]!) // 5 10 15
}

Create Dictionary<String, [SomeStruct]> from [SomeStruct] source-array

var sourceEntries: [Entry] = [entry1, ..., entry14]
var myDict: Dictionary<String, [Entry]> = [:]
for entry in sourceEntries {
if var array = myDict[entry.attribute1] { theArray.append(entry) }
else { myDict[entry.attribute1] = [entry] }
}
I am intending to create a Dictionary, which matches all the objects of the struct "Eintrag" with the same attribute from the source-Array "alleEinträge" to a String containing the value of the shared attribute. For some reason my final Dictionary just matches Arrays of one element to the Strings, although some Arrays ought to contain up to four elements.
The problem is that the array is passed by value (i.e. "copied"), so the array you are writing to when you say array.append is not the array that is "inside" the dictionary. You have to write back into the dictionary explicitly if you want to change what's in it.
Try it in a simple situation:
var dict = ["entry":[0,1,2]]
// your code
if var array = dict["entry"] { array.append(4) }
// so what happened?
println(dict) // [entry: [0, 1, 2]]
As you can see, the "4" never got into the dictionary.
You have to write back into the dictionary explicitly:
if var array = dict["entry"] { array.append(4); dict["entry"] = array }
FURTHER THOUGHTS: You got me thinking about whether there might be a more elegant way to do what you're trying to do. I'm not sure whether you will think this is "more elegant", but perhaps it has some appeal.
I will start by setting up a struct (like your Entry) with a name attribute:
struct Thing : Printable {
var name : String
var age : Int
var description : String {
return "{\(self.name), \(self.age)}"
}
}
Now I will create an array like your sourceEntries array, where some of the structs share the same name (like your shared attribute attribute1):
let t1 = Thing(name: "Jack", age: 40)
let t2 = Thing(name: "Jill", age: 38)
let t3 = Thing(name: "Jill", age: 37)
let arr = [t1,t2,t3]
And of course I will prepare the empty dictionary, like your myDict, which I call d:
var d = [String : [Thing]]()
Now I will create the dictionary! The idea is to use map and filter together to do all the work of creating key-value pairs, and then we just build the dictionary from those pairs:
let pairs : [(String, [Thing])] = arr.map {
t in (t.name, arr.filter{$0.name == t.name})
}
for pair in pairs { d[pair.0] = pair.1 }

How do I get the key at a specific index from a Dictionary in Swift?

I have a Dictionary in Swift and I would like to get a key at a specific index.
var myDict : Dictionary<String,MyClass> = Dictionary<String,MyClass>()
I know that I can iterate over the keys and log them
for key in myDict.keys{
NSLog("key = \(key)")
}
However, strangely enough, something like this is not possible
var key : String = myDict.keys[0]
Why ?
That's because keys returns LazyMapCollection<[Key : Value], Key>, which can't be subscripted with an Int. One way to handle this is to advance the dictionary's startIndex by the integer that you wanted to subscript by, for example:
let intIndex = 1 // where intIndex < myDictionary.count
let index = myDictionary.index(myDictionary.startIndex, offsetBy: intIndex)
myDictionary.keys[index]
Another possible solution would be to initialize an array with keys as input, then you can use integer subscripts on the result:
let firstKey = Array(myDictionary.keys)[0] // or .first
Remember, dictionaries are inherently unordered, so don't expect the key at a given index to always be the same.
Swift 3 : Array() can be useful to do this .
Get Key :
let index = 5 // Int Value
Array(myDict)[index].key
Get Value :
Array(myDict)[index].value
Here is a small extension for accessing keys and values in dictionary by index:
extension Dictionary {
subscript(i: Int) -> (key: Key, value: Value) {
return self[index(startIndex, offsetBy: i)]
}
}
You can iterate over a dictionary and grab an index with for-in and enumerate (like others have said, there is no guarantee it will come out ordered like below)
let dict = ["c": 123, "d": 045, "a": 456]
for (index, entry) in enumerate(dict) {
println(index) // 0 1 2
println(entry) // (d, 45) (c, 123) (a, 456)
}
If you want to sort first..
var sortedKeysArray = sorted(dict) { $0.0 < $1.0 }
println(sortedKeysArray) // [(a, 456), (c, 123), (d, 45)]
var sortedValuesArray = sorted(dict) { $0.1 < $1.1 }
println(sortedValuesArray) // [(d, 45), (c, 123), (a, 456)]
then iterate.
for (index, entry) in enumerate(sortedKeysArray) {
println(index) // 0 1 2
println(entry.0) // a c d
println(entry.1) // 456 123 45
}
If you want to create an ordered dictionary, you should look into Generics.
From https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/CollectionTypes.html:
If you need to use a dictionary’s keys or values with an API that takes an Array instance, initialize a new array with the keys or values property:
let airportCodes = [String](airports.keys) // airportCodes is ["TYO", "LHR"]
let airportNames = [String](airports.values) // airportNames is ["Tokyo", "London Heathrow"]
SWIFT 3. Example for the first element
let wordByLanguage = ["English": 5, "Spanish": 4, "Polish": 3, "Arabic": 2]
if let firstLang = wordByLanguage.first?.key {
print(firstLang) // English
}
In Swift 3 try to use this code to get Key-Value Pair (tuple) at given index:
extension Dictionary {
subscript(i:Int) -> (key:Key,value:Value) {
get {
return self[index(startIndex, offsetBy: i)];
}
}
}
SWIFT 4
Slightly off-topic: But here is if you have an
Array of Dictionaries i.e: [ [String : String] ]
var array_has_dictionary = [ // Start of array
// Dictionary 1
[
"name" : "xxxx",
"age" : "xxxx",
"last_name":"xxx"
],
// Dictionary 2
[
"name" : "yyy",
"age" : "yyy",
"last_name":"yyy"
],
] // end of array
cell.textLabel?.text = Array(array_has_dictionary[1])[1].key
// Output: age -> yyy
Here is an example, using Swift 1.2
var person = ["name":"Sean", "gender":"male"]
person.keys.array[1] // "gender", get a dictionary key at specific index
person.values.array[1] // "male", get a dictionary value at specific index
I was looking for something like a LinkedHashMap in Java. Neither Swift nor Objective-C have one if I'm not mistaken.
My initial thought was to wrap my dictionary in an Array. [[String: UIImage]] but then I realized that grabbing the key from the dictionary was wacky with Array(dict)[index].key so I went with Tuples. Now my array looks like [(String, UIImage)] so I can retrieve it by tuple.0. No more converting it to an Array. Just my 2 cents.

Resources