How to loop over a dictionary in Swift 5? - swift

In Swift 4 I could the code below worked but in Swift 5 I get the following error: Type 'Dictionary<String, String>.Values.Iterator' does not conform to protocol 'Sequence'
guard let userIds = users.values.makeIterator() else { return }
for userId in userIds {
// User setup
}
What is the right way in Swift 5 now?

let dictionary: [String: Int] = ["a": 1, "b": 2]
for (key, value) in dictionary {
print(key, value)
}

You can do simply
for (_, userId) in users {
// User setup
}

You may try iterator like this:
let users = ["a":11, "b":12]
var userIds = users.values.makeIterator()
while let next = userIds.next() {
print(next) // 11 \n 12
}

Also:
let x = [
"kitty": 7,
"bob": 2,
"orange": 44
]
x.forEach { t in
print("key = \(t.key); value = \(t.value)")
}
This has apparently been available since Swift 3. Link to standard library docs on Dictionary type.

Swift 4, swift5 and above
let dict: [String: Any] = ["a": 1, "b": "hello", "c": 3]
for (key, value) in dict {
print(key, value)
}

Related

How to update nested dictionary in swift [duplicate]

This question already has answers here:
How to update a value in a nested dictionary given path fragment in Swift?
(2 answers)
Closed 3 years ago.
I have nested dictionary, so something like this:
var dict = ["a":["b":"c", "d":["e":"f"]], "f":["b":"g"]] // and more
Now if I want to update the value of key a.d.e, how should I do that?
It seems updateValue method only reads key as it is...It doesn't know anything about the nested key.
Edit:
While I really want to change the structure of this data to something easier to work with. I can't. This belongs to another class and I am not allow to change the structure, all I can do is to update it.
Second Edit:
After some thoughts and read the other question some one point out might be a duplicate, tried recursively updating. I feel this is seriously the worst way to do it because essentially it's creating new dictionaries with a copy of original value and assign it back. I do think it is a waste of space and tbh even recursively calling this I feel is unnecessary.
func updateKey(key:[AnyHashable],val:Bool,data:[AnyHashable:Any])->[AnyHashable:Any]{
var keyTemp = key
var tempData = data
if(keyTemp.count==1){
tempData.updateValue(val, forKey: key[0])
print(tempData)
return tempData
}else{
var firstLayerValue = data[keyTemp[0]] as? [AnyHashable:Any]
var firstKey = keyTemp.removeFirst()
var tempResult = updateKey(key: keyTemp, val: val, data: firstLayerValue!)
tempData.updateValue(tempResult, forKey: firstKey)
return tempData;
}
}
This returns a copy of what I intend to do, and I have to actually assign it back to the original copy. I really don't like this assigning back and forth thing, what if something went wrong in the middle then I might just end up losing the originally correct data.
Is there any better solution?
Another way of doing it
dict["a"]?["d"] = ["e": 123]
print(dict)
Outputs
["f": ["b": "g"], "a": ["b": "c", "d": ["e": 123]]]
Structs are the way to go. But if you really have to use dictionaries, here is a workaround:
var dict: [AnyHashable: Any] =
["a": ["b": "c", "d": ["e":"f"]],
"f": ["b": "g"]]
let newValue = true
if var firstLevel = dict["a"] as? [String : Any],
var secondLevel = firstLevel["d"] as? [String: Any] {
secondLevel["e"] = newValue
firstLevel["d"] = secondLevel
dict["a"] = firstLevel
}
print(dict) //[AnyHashable("a"): ["d": ["e": true], "b": "c"], AnyHashable("f"): ["b": "g"]]
To update a value in a dictionary with multiple levels, you can define a function like so:
func update(dictionary dict: inout [AnyHashable: Any], at keys: [AnyHashable], with value: Any) {
if keys.count < 2 {
for key in keys { dict[key] = value }
return
}
var levels: [[AnyHashable: Any]] = []
for key in keys.dropLast() {
if let lastLevel = levels.last {
if let currentLevel = lastLevel[key] as? [AnyHashable: Any] {
levels.append(currentLevel)
}
else if lastLevel[key] != nil, levels.count + 1 != keys.count {
break
} else { return }
} else {
if let firstLevel = dict[keys[0]] as? [AnyHashable : Any] {
levels.append(firstLevel )
}
else { return }
}
}
if levels[levels.indices.last!][keys.last!] != nil {
levels[levels.indices.last!][keys.last!] = value
} else { return }
for index in levels.indices.dropLast().reversed() {
levels[index][keys[index + 1]] = levels[index + 1]
}
dict[keys[0]] = levels[0]
}
And use it like so:
var dict: [AnyHashable: Any] = ["a": ["b": 1,
"c": ["d": ["e": "f"],
"g": ["h": 1.5]]],
"j": ["k": 2]]
update(dictionary: &dict,
at: ["a", "c", "d", "e"],
with: true)
dict.forEach { print($0) }
Here is the output in the console:
(key: AnyHashable("a"), value: [AnyHashable("b"): 1, AnyHashable("c"): [AnyHashable("d"): [AnyHashable("e"): true], AnyHashable("g"): ["h": 1.5]]])
(key: AnyHashable("j"), value: ["k": 2])

Accessing AnyCodable values with #dynamicMemberLookup

Objc.io has a great talk on easily mutating untyped Dictionaries, but the problem is you can't easily persist them. I think the talk may have been released before #dynamicMemberLookup was introduced.
AnyCodable looks awesome for easily encoding/decoding/persisting simple Dictionaries but you can't easily access the Dictionary members.
I was wondering if it's possible/feasible to add #dynamicMemberLookup functionality found in Swift 4.2 (eg: in this example) to AnyCodable and, if so, how? The end goal would be to access/mutate an untyped Array or Dictionary and persist them.
So, I tried doing it like this:
#dynamicMemberLookup
public struct AnyCodable: Codable {
public let value: Any
public init<T>(_ value: T?) {
self.value = value ?? ()
}
subscript(dynamicMember member: String) -> AnyCodable? {
switch self.value {
case let dictionary as [String: Any?]:
return AnyCodable(dictionary[member])
default:
return nil
}
}
}
Given the example Dictionary from AnyCodable:
let dictionary: [String: AnyEncodable] = [
"boolean": true,
"integer": 1,
"double": 3.14159265358979323846,
"string": "string",
"array": [1, 2, 3],
"nested": [
"a": "alpha",
"b": "bravo",
"c": "charlie"
]
]
If I do:
if let nested = dictionary["nested"] {
print("nested a:", nested.a)
}
it outputs: nested a: Optional(AnyCodable(Optional("alpha"))) which is almost there! But I want to be able to simply write dictionary?.nested?.a OR dictionary?.array?[1] rather than unwrap nested first with if let nested = dictionary["nested"]. And I want to be able to mutate it, eg: dictionary?.nested?.a? = "beta".
I can't figure out how to get it across the finish line though. I'd obviously need to add case let array as [Any]: etc. and maybe change the subscript to include getter/setters? But what else am I missing?
I know you're probably "not supposed to use Dictionaries that way" and create a full-blown custom-typed model and all that, but this is for a small project where going that route would be overkill. So please don't answer with "model your data differently". I want to combine these two existing methods of accessing/persisting untyped dictionaries or arrays into one.
Ok, I think I had it mostly covered.
The first problem is, that you work with a dictionary. You can add #dynamicMemberLookup only to the main definition, so you can't do it on the dictionary definition. Try this:
let dictionary: [String: AnyEncodable] = [ ... ]
let easierToUse = AnyCodable(dictionary)
So considering code below, is it what you needed? :
let dictionary: [String: AnyCodable] = [
"boolean": true,
"integer": 1,
"double": 3.14159265358979323846,
"string": "string",
"array": [1, 2, 3],
"nested": [
"a": "alpha",
"b": "bravo",
"c": "charlie",
"array": [
1,
2,
[
"a": "alpha",
"b": "bravo",
"c": "deep charlie"
]
],
]
]
let easierToUse: AnyCodable = AnyCodable(dictionary)
if let value = easierToUse.nested?.a {
print(value) // prints "alpha"
}
if let value = easierToUse.nested?.array?[2]?.c {
print(value) // prints "deep charlie"
}
if let value = easierToUse.nested?.array?[2]?.c?.value as? String {
print(value) // prints "deep charlie"
}
I had to update your classes a bit, as you forgot that it is all wrapped on every level:
// Helper to handle out of bounds on array with nil
extension Array {
subscript (safe index: Int) -> Element? {
return indices ~= index ? self[index] : nil
}
}
#dynamicMemberLookup
public struct AnyCodable: Codable {
public let value: Any
public init<T>(_ value: T) {
self.value = value
}
public init<T>(_ value: T?) {
self.value = value ?? ()
}
subscript(dynamicMember member: String) -> AnyCodable? {
switch self.value {
case let anyCodable as AnyCodable:
return anyCodable[dynamicMember: member]
case let dictionary as [String: Any?]:
return AnyCodable(dictionary[member] ?? nil)
default:
return nil
}
}
subscript(index: Int) -> AnyCodable? {
switch self.value {
case let anyCodable as AnyCodable:
return anyCodable[index]
case let array as [Any]:
return AnyCodable(array[safe: index])
default:
return nil
}
}
}

How could I remove just one dictionary which I know the value?

I want to remove the row which value = 0 in swift
I want to remove the Firebase data from client.
But don't know how to remove just one dictionary (just like the attachment)
Help!!!
Version:
Xcode 9.1,Swift 4.0
What ever the response your getting you can manipulate from your side by converting into Dictionary and manipulates like:
var dict = ["key1": 0, "key2": 2]
for (key, value) in dict {
if value == 0 {
dict.removeValue(forKey: k)
}
}
print(dict)
OR
var dict = ["key1": 0, "key2": 2]
dict = dict.filter { $0.value != 0 }
print(dict)
output for both :
["key2": 2]

Swift Warning - Cast from '[(key: String, value: Int)]' to unrelated type '[String : Int]' always fails

Given the dictionary:
let dictionary = [ "one": 1, "two": 2, "three": 3]
I want to create a new version with one of the items removed based on its key. So I'm trying to use...
let dictionaryWithTwoRemoved = dictionary.filter { $0.0 != "two" }
... which achieves what I want HOWEVER the two dictionaries have differing types...
`dictionary` is a `[String: Int]`
`dictionaryWithTwoRemoved` is a `[(key: String, value: Int)]`
Which is consequently making my life difficult.
If I try to cast like so...
let dictionaryWithThreeRemoved = dictionary.filter { $0.0 != "three" } as! [String: Int]
...I get the following WARNING...
Cast from '[(key: String, value: Int)]' to unrelated type '[String :
Int]' always fails
and the code also crashes with EXC_BAD_INSTRUCTION at runtime.
Help!
If you want an extension method to help you remove the values here you go...
extension Dictionary {
func removingValue(forKey key: Key) -> [Key: Value] {
var mutableDictionary = self
mutableDictionary.removeValue(forKey: key)
return mutableDictionary
}
}
You can use reduce to do this.
//: Playground - noun: a place where people can play
import Cocoa
let dictionary = [ "one": 1, "two": 2, "three": 3]
let newDictionary = dictionary.reduce([:]) { result, element -> [String: Int] in
guard element.key != "two" else {
return result
}
var newResult = result
newResult[element.key] = element.value
return newResult
}

Sort Dictionary by values in Swift

Is there are analog of - (NSArray *)keysSortedByValueUsingSelector:(SEL)comparator in swift?
How to do this without casting to NSDictionary?
I tried this, but it seems to be not a good solution.
var values = Array(dict.values)
values.sort({
$0 > $1
})
for number in values {
for (key, value) in dict {
if value == number {
println(key + " : \(value)");
dict.removeValueForKey(key);
break
}
}
}
Example:
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
dict.sortedKeysByValues(>) // fanta (12), cola(10), sprite(8)
Just one line code to sort dictionary by Values in Swift 4, 4.2 and Swift 5:
let sortedByValueDictionary = myDictionary.sorted { $0.1 < $1.1 }
Try:
let dict = ["a":1, "c":3, "b":2]
extension Dictionary {
func sortedKeys(isOrderedBefore:(Key,Key) -> Bool) -> [Key] {
return Array(self.keys).sort(isOrderedBefore)
}
// Slower because of a lot of lookups, but probably takes less memory (this is equivalent to Pascals answer in an generic extension)
func sortedKeysByValue(isOrderedBefore:(Value, Value) -> Bool) -> [Key] {
return sortedKeys {
isOrderedBefore(self[$0]!, self[$1]!)
}
}
// Faster because of no lookups, may take more memory because of duplicating contents
func keysSortedByValue(isOrderedBefore:(Value, Value) -> Bool) -> [Key] {
return Array(self)
.sort() {
let (_, lv) = $0
let (_, rv) = $1
return isOrderedBefore(lv, rv)
}
.map {
let (k, _) = $0
return k
}
}
}
dict.keysSortedByValue(<)
dict.keysSortedByValue(>)
Updated:
Updated to the new array syntax and sort semantics from beta 3. Note that I'm using sort and not sorted to minimize array copying. The code could be made more compact, by looking at the earlier version and replacing sort with sorted and fixing the KeyType[] to be [KeyType]
Updated to Swift 2.2:
Changed types from KeyType to Key and ValueType to Value. Used new sort builtin to Array instead of sort(Array) Note performance of all of these could be slightly improved by using sortInPlace instead of sort
You could use something like this perhaps:
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
var myArr = Array(dict.keys)
var sortedKeys = sort(myArr) {
var obj1 = dict[$0] // get ob associated w/ key 1
var obj2 = dict[$1] // get ob associated w/ key 2
return obj1 > obj2
}
myArr // ["fanta", "cola", "sprite"]
This should give you the sorted keys based on value, and is a little more cleaner:
var sortedKeys = Array(dict.keys).sorted(by: { dict[$0]! < dict[$1]! })
I think this is the easiest way to sort Swift dictionary by value.
let dict = ["apple":1, "cake":3, "banana":2]
let byValue = {
(elem1:(key: String, val: Int), elem2:(key: String, val: Int))->Bool in
if elem1.val < elem2.val {
return true
} else {
return false
}
}
let sortedDict = dict.sort(byValue)
OneLiner :
let dict = ["b": 2, "a": 1, "c": 3]
(Array(dict).sorted { $0.1 < $1.1 }).forEach { (k,v) in print("\(k):\(v)") }
//Output: a:1, b:2, c:3
Swap out the .forEach with .map -> Functional programming
Syntactical sugar :
extension Dictionary where Value: Comparable {
var sortedByValue: [(Key, Value)] { return Array(self).sorted { $0.1 < $1.1} }
}
extension Dictionary where Key: Comparable {
var sortedByKey: [(Key, Value)] { return Array(self).sorted { $0.0 < $1.0 } }
}
["b": 2, "a": 1, "c": 3].sortedByKey // a:1, b:2, c:3
["b": 2, "a": 1, "c": 3].sortedByValue // a:1, b:2, c:3
Lots of answers, here's a one-liner. I like it because it makes full use of native Swift iterative functions and doesn't use variables. This should help the optimiser do its magic.
return dictionary.keys.sort({ $0 < $1 }).flatMap({ dictionary[$0] })
Note the use of flatMap, because subscripting a dictionary returns an optional value. In practice this should never return nil since we get the key from the dictionary itself. flatMap is there only to ensure that the result is not an array of optionals. If your array's associated value should BE an optional you can use map instead.
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. I have yet to find a quicker method, other than the same approach in form of a simple one-liner:
let yourDict = ["One": "X", "Two": "B", "Three": "Z", "Four": "A"]
let sortedKeys = yourDict.keys.sort { yourDict[$0] < yourDict[$1] }
Sorting a dictionary by key or value
Using Swift 5.2 internal handling of "sorted":
var unsortedDict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
// sorting by value
let sortedDictByValue = unsortedDict.sorted{ $0.value > $1.value } // from lowest to highest using ">"
print("sorted dict: \(sortedDictByValue)")
// result: "sorted dict: [(key: "fanta", value: 12), (key: "cola", value: 10), (key: "sprite", value: 8)]\n"
// highest value
print(sortedDictByValue.first!.key) // result: fanta
print(sortedDictByValue.first!.value) // result: 12
// lowest value
print(sortedDictByValue.last!.key) // result: sprite
print(sortedDictByValue.last!.value) // result: 8
// by index
print(sortedDictByValue[1].key) // result: cola
print(sortedDictByValue[1].value) // result: 10
// sorting by key
let sortedDictByKey = unsortedDict.sorted{ $0.key < $1.key } // in alphabetical order use "<"
// alternative:
// let sortedDictByKey = unsortedDict.sorted{ $0 < $1 } // without ".key"
print("sorted dict: \(sortedDictByKey)")
// result: "sorted dict: [(key: "cola", value: 10), (key: "fanta", value: 12), (key: "sprite", value: 8)]\n"
// highest value
print(sortedDictByKey.first!.key) // result: cola
print(sortedDictByKey.first!.value) // result: 10
// lowest value
print(sortedDictByKey.last!.key) // result: sprite
print(sortedDictByKey.last!.value) // result: 8
// by index
print(sortedDictByKey[1].key) // result: fanta
print(sortedDictByKey[1].value) // result: 12
The following might be useful if you want the output to be an array of key value pairs in the form of a tuple, sorted by value.
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
let sortedArrByValue = dict.sorted{$0.1 > $1.1}
print(sortedArrByValue) // output [(key: "fanta", value: 12), (key: "cola", value: 10), (key: "sprite", value: 8)]
Since Swift 3.0 Dictionary has sorted(by:) function which returns an array of tuples ([(Key, Value)]).
let sorted = values.sorted(by: { (keyVal1, keyVal2) -> Bool in
keyVal1.value > keyVal2.value
})
Just cast it to NSDictionary and then call the method. Anywhere you use #selector in ObjC you can just use a String in Swift. So it would look like this:
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
let sortedKeys = (dict as NSDictionary).keysSortedByValueUsingSelector("compare:")
or
let sortedKeys2 = (dict as NSDictionary).keysSortedByValueUsingComparator
{
($0 as NSNumber).compare($1 as NSNumber)
}
As of Swift 3, to sort your keys based on values, the below looks promising:
var keys = Array(dict.keys)
keys.sortInPlace { (o1, o2) -> Bool in
return dict[o1]! as! Int > dict[o2]! as! Int
}
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
let arr = dic.sort{ (d1,d2)-> Bool in
if d1.value > d2.value {
retrn true
}
}.map { (key,value) -> Int in
return value
}
Take look a clean implementation way.
print("arr is :(arr)")
The following way in Swift 3 sorted my dictionary by value in the ascending order:
for (k,v) in (Array(dict).sorted {$0.1 < $1.1}) {
print("\(k):\(v)")
}
SWIFT 3:
Using a few resources I put this beautifully short code together.
dictionary.keys.sorted{dictionary[$0]! < dictionary[$1]!}
This returns an array of the dictionary keys sorted by their values. It works perfectly & doesn't throw errors when the dictionary is empty. Try this code in a playground:
//: Playground - noun: a place where people can play
import UIKit
let dictionary = ["four": 4, "one": 1, "seven": 7, "two": 2, "three": 3]
let sortedDictionary = dictionary.keys.sorted{dictionary[$0]! < dictionary[$1]!}
print(sortedDictionary)
// ["one", "two", "three", "four", "seven"]
let emptyDictionary = [String: Int]()
let emptyDictionarySorted = emptyDictionary.keys.sorted{emptyDictionary[$0]! < emptyDictionary[$1]!}
print(emptyDictionarySorted)
// []
If you'd like some help on why the heck the code uses $0, $1 and doesn't even have parentheses after the "sorted" method, check out this post - https://stackoverflow.com/a/34785745/7107094
This is how I did it - sorting in this case by a key called position. Try this in a playground:
var result: [[String: AnyObject]] = []
result.append(["name" : "Ted", "position": 1])
result.append(["name" : "Bill", "position": 0])
result
result = sorted(result, positionSort)
func positionSort(dict1: [String: AnyObject], dict2: [String: AnyObject]) -> Bool {
let position1 = dict1["position"] as? Int ?? 0
let position2 = dict2["position"] as? Int ?? 0
return position1 < position2
}
Sorting the dictionary with a dictionary as the value (Nested dictionary)
var students: [String: [String: Any?]] = ["16CSB40" : ["Name": "Sunitha", "StudentId": "16CSB40", "Total": 90], "16CSB41" : ["Name": "Vijay", "StudentId": "16CSB40", "Total": 80], "16CSB42" : ["Name": "Tony", "StudentId": "16CSB42", "Total": 95]] // Sort this dictionary with total value
let sorted = students.sorted { (($0.1["Total"] as? Int) ?? 0) < (($1.1["Total"] as? Int) ?? 0) }
print(sorted) //Sorted result
Use this, and then just loop through the dictionary again using the output keys.
extension Dictionary where Value: Comparable {
func sortedKeysByValue() -> [Key] {
keys.sorted { return self[$0]! < self[$1]! }
}
}
...or this if you hate force unwrapping :)
extension Dictionary where Value: Comparable {
func sortedKeysByValue() -> [Key] {
keys.sorted { (key1, key2) -> Bool in
guard let val1 = self[key1] else { return true }
guard let val2 = self[key2] else { return true }
return val1 < val2
}
}
}