Getting values out of dictionaries which have arrays as values - swift

I have a dictionary which contains languages as values and the initial character of every language name (A, B, C, ...) as for the key.
var dictionary = [Character: [Language]]()
I would like to get all the languages out of the dictionary in the form of an array. To do so, I do
let languages = dictionary.values // Dictionary<Character, [Language]>.Values
That's not an array. I try to get the array like this
let languages = Array(tableViewSource.values) // [[Language]]
This returns an array of an array. How do I just get an array of languages? I saw the merge(_:uniquingKeysWith:) but I don't need to merge dictionaries.

You can try
let allLangs = nestedArr.reduce([], +)

Martin R's and Sh_Khan's answers outline the standard ways of doing this (you basically just want to flatten an array). However I'd like to show a more "manual" approach:
var langs = [String]()
for lang_list in dictionary.values {
langs.append(contentsOf: lang_list)
}
Alternatively, you can use the += operator instead of .append(contentsOf:). An easier way of doing this would be using flatMap(_:):
dictionary.values.flatMap { $0 }

If you want a single array (concatenating all dictionary values) then
Array(dictionary.values.joined())
does the trick. (This creates the final array without creating any additional intermediate arrays.)
Example:
let dictionary: [Character: [String]] = ["E": ["espanol", "english"], "G": ["german", "greek"]]
let langs = Array(dictionary.values.joined())
print(langs) // ["german", "greek", "espanol", "english"]
Note that the order of key/value pairs in a dictionary is unspecified.
An alternative is
let dictionary: [Character: [String]] = ["E": ["espanol", "english"], "G": ["german", "greek"]]
let langs = dictionary.keys.sorted().flatMap { dictionary[$0]! }
print(langs) // ["espanol", "english", "german", "greek"]
which gives the languages sorted by the corresponding keys.

Related

Add two dictionary values in new array . the array values will remains unchanged from its position

Add two dictionary values in a new array. the array values will remain unchanged from its position. I need to add two dictionary values in a new array. The values added in the array must remain constant at every run.
var dictionary1:[String:Int] = ["Mohan":75, "Raghu":82, "John":79]
var dictionary2:[String:Int] = ["Surya":91, "John":79, "Saranya":92]
dictionary1.merge(dictionary2){(current, _) in current}
var arr : [String] = []
for (key, value) in dictionary1 { arr.append("(key) (value)") }
print(dictionary1)
Simply use append(contentsOf:) to add the contents of dictionary1 and dictionary2 to arr. Use map(_:) to format the key-value pairs while adding to the array.
let dictionary1 = ["Mohan":75, "Raghu":82, "John":79]
let dictionary2 = ["Surya":91, "John":79, "Saranya":92]
var arr = [String]()
arr.append(contentsOf: dictionary1.map({"\($0.key) \($0.value)"}))
arr.append(contentsOf: dictionary2.map({"\($0.key) \($0.value)"}))
print(arr) //["Mohan 75", "John 79", "Raghu 82", "Surya 91", "John 79", "Saranya 92"]
As apple stats, you can use KeyValuePairs to maintain ordered collection of key-value pairs and don’t require the fast key lookup that the Dictionary type provides.
let recordTimes: KeyValuePairs = ["Florence Griffith-Joyner": 10.49,
"Evelyn Ashford": 10.76,
"Evelyn Ashford": 10.79,
"Marlies Gohr": 10.81]
print(recordTimes.first!)
// Prints "("Florence Griffith-Joyner", 10.49)"
Check if it is helpful in your case.

Basic Dictionary Operations in Swift [duplicate]

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:+)

Most efficient way to flatten an array of dictionaries containing arrays

Let's say I have an array of dictionaries, and each contains an array of letters. Like this:
let dicts = [["letters" : ["a","b","c"]],
["letters" : ["d","e","f"]]]
What is the most efficient way to create a flattened array of all the letters from all the dictionaries?
You can use reduce(_:_:) for that.
let array = dicts.reduce([]) { $0 + ($1["letters"] ?? []) }
print(array) // ["a","b","c","d","e","f"]
Edit: As #Hamish suggested a link in comment simplest solution is having less time, so if you are having large amount of data then you can use forEach closure with array.
var result = [String]()
dicts.forEach {
result.append(contentsOf: $0["letters"] ?? [])
}
You can use flatMap() to map each dictionary to the corresponding
letters array and join the results:
let dicts = [["letters" : ["a","b","c"]],
["letters" : ["d","e","f"]]]
let letters = Array(dicts.flatMap { $0["letters"] }.joined())
print(letters) // ["a", "b", "c", "d", "e", "f"]
This is effective insofar as no additional intermediate arrays are
created during the join. As #Hamish pointed out below, the
intermediate [[String]] can be avoided with
let letters = Array(dicts.lazy.flatMap { $0["letters"] }.joined())
You can use any one of these. You don't need to specify the key name of the dictionary.
Solution 1
let array = dicts.reduce([]) { $0 + ($1.values.reduce([]) { $0 + $1 }) }
print(array) // ["a","b","c","d","e","f"]
Solution 2
let array = dicts.flatMap { $0.values.joined() }
print(array) // ["a","b","c","d","e","f"]

How to sort dictionary by value?

My dictionary like this:
var items = [Int: [String]]()
var itemsResult = [Int: [String]]()
itmesResult stores the data downloaded from server.
and pass the data to items use of items = itmesResult
the value has 3 elements like ["Apple","/image/apple.png","29"]
and I want to sort the dictionary by first value which is Apple.
for (k,v) in (itemsResult.sorted(by: { $0.value[0] < $1.value[0] })) {
items[k] = v
}
The result of above code is not my expectation.
I would like to sort it alphabetically how can I do this?
Edit:
origin:
1:["Apple","/image/apple.png","29"]
2:["AAA","/image/aaa.png","29"]
3:["Banana","/image/banana.png","29"]
sorted:
2:["AAA","/image/aaa.png","29"]
1:["Apple","/image/apple.png","29"]
3:["Banana","/image/banana.png","29"]
I would like to sort it by first value.
So if I take your example, this does the trick:
var items = [Int: [String]]()
items[0] = ["Apple","/image/apple.png","29"]
items[1] = ["AAA","/image/aaa.png","29"]
items[2] = ["Banana","/image/banana.png","29"]
let itemResult = items.sorted { (first: (key: Int, value: [String]), second: (key: Int, value: [String])) -> Bool in
return first.value.first! < second.value.first!
}
print (itemResult)
The right thing to do is to use objects of course, and note that I'm not null checking the "first" object in each array, but this is not a problem to change.
Let me know if this is what you were looking for, the output is:
[(1, ["AAA", "/image/aaa.png", "29"]), (0, ["Apple", "/image/apple.png", "29"]), (2, ["Banana", "/image/banana.png", "29"])]
EDIT:
Also note, that this case doesn't actually "sort" the dictionary, because a dictionary is by definition not sorted, this creates an array of key-value objects sorted using the array indexes
Instead of saving these variable into an array of arrays, make them an array of dictionaries.
You can do this like so:
var dictionaries:[Dictionary<String, String>] = []
for item in items {
let dictionary = {"name": item[0], "url": item[1], "id" : item[2]}
dictionaries.append(dictionary)
}
You can get your sorted list of dictionaries like this:
dictionaries.sorted(by: { $0["name"] < $1["name"] })

How to extract a subset of a swift 3 Dictionary

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)]