Map a Dictionary with array of values - swift

I want to convert a dictionary to an Array, by showing each [String: [String]] element as a string in the array (but the value may be empty).
So ["A": ["1","2"], "b": [], "c": ["5", "6"]] will give ["b", "A1", "A2", "c5", "c6"].
I want to use map to do this, as the code I have feels unwieldy:
let messages: [String: [String]] = ["A": ["1","2"], "b": [], "c": ["5", "6"]]
var result: [String] = []
for message in messages {
if !message.value.isEmpty {
for value in message.value {
result.append(message.key + value)
}
} else {
result.append(message.key)
}
}
How can I create the solution using map?

A little bit shortened version using map
let messages: [String: [String]] = ["A": ["1","2"], "b": [], "c": ["5", "6"]]
let result = messages.map { dict in
return dict.value.isEmpty ? ["\(dict.key)"] : dict.value.map { "\(dict.key)\($0)" }
}.flatMap{$0}

A possible solution is to use reduce(into:_:), (I prefer this one):
let messages: [String: [String]] = ["A": ["1","2"], "b": [], "c": ["5", "6"]]
let output = messages.reduce(into: [String]()) { result, current in
guard !current.value.isEmpty else { result.append(current.key); return }
let sub = current.value.map { current.key + $0 }
result.append(contentsOf: sub)
}
print("Output: \(output)")
Output:
$> Output: ["c5", "c6", "A1", "A2", "b"]
With a map and a flatMap, I found it less intuitive:
let output2 = messages.map { element -> [String] in
guard !element.value.isEmpty else { return [element.key] }
let sub = element.value.map { element.key + $0 }
return sub
}.flatMap { $0 }
print("Output2: \(output2)")
Which then can be simplified with a direct call to flatMap, but that (map + flatMap) was to show the process, since this one will save you an iteration (you do only one instead of two):
let output3 = messages.flatMap { element -> [String] in
guard !element.value.isEmpty else { return [element.key] }
let sub = element.value.map { element.key + $0 }
return sub
}
print("Output3: \(output3)")
I made the closure explicit enough, but that's up to you if you want to shorten them with ternary if etc.

Related

How can I sort dictionary [duplicate]

I want to sort a dictionary in Swift. I have a dictionary like:
"A" => Array[]
"Z" => Array[]
"D" => Array[]
etc. I want it to be like
"A" => Array[]
"D" => Array[]
"Z" => Array[]
etc.
I have tried many solutions on SO but no one worked for me. I am using XCode6 Beta 5 and on it some are giving compiler error and some solutions are giving exceptions. So anyone who can post the working copy of dictionary sorting.
let dictionary = [
"A" : [1, 2],
"Z" : [3, 4],
"D" : [5, 6]
]
let sortedKeys = Array(dictionary.keys).sorted(<) // ["A", "D", "Z"]
EDIT:
The sorted array from the above code contains keys only, while values have to be retrieved from the original dictionary. However, 'Dictionary' is also a 'CollectionType' of (key, value) pairs and we can use the global 'sorted' function to get a sorted array containg both keys and values, like this:
let sortedKeysAndValues = sorted(dictionary) { $0.0 < $1.0 }
println(sortedKeysAndValues) // [(A, [1, 2]), (D, [5, 6]), (Z, [3, 4])]
EDIT2: The monthly changing Swift syntax currently prefers
let sortedKeys = Array(dictionary.keys).sort(<) // ["A", "D", "Z"]
The global sorted is deprecated.
To be clear, you cannot sort Dictionaries. But you can out put an array, which is sortable.
Swift 2.0
Updated version of Ivica M's answer:
let wordDict = [
"A" : [1, 2],
"Z" : [3, 4],
"D" : [5, 6]
]
let sortedDict = wordDict.sort { $0.0 < $1.0 }
print("\(sortedDict)") //
Swift 3
wordDict.sorted(by: { $0.0 < $1.0 })
In Swift 5, in order to sort Dictionary by KEYS
let sortedYourArray = YOURDICTIONARY.sorted( by: { $0.0 < $1.0 })
In order to sort Dictionary by VALUES
let sortedYourArray = YOURDICTIONARY.sorted( by: { $0.1 < $1.1 })
If you want to iterate over both the keys and the values in a key sorted order, this form is quite succinct
let d = [
"A" : [1, 2],
"Z" : [3, 4],
"D" : [5, 6]
]
Swift 1,2:
for (k,v) in Array(d).sorted({$0.0 < $1.0}) {
println("\(k):\(v)")
}
Swift 3+:
for (k,v) in Array(d).sorted(by: {$0.0 < $1.0}) {
println("\(k):\(v)")
}
I tried all of the above, in a nutshell all you need is
let sorted = dictionary.sorted { $0.key < $1.key }
let keysArraySorted = Array(sorted.map({ $0.key }))
let valuesArraySorted = Array(sorted.map({ $0.value }))
In swift 4 you can write it smarter:
let d = [ 1 : "hello", 2 : "bye", -1 : "foo" ]
d = [Int : String](uniqueKeysWithValues: d.sorted{ $0.key < $1.key })
Swift 4 & 5
For string keys sorting:
dictionary.keys.sorted(by: {$0.localizedStandardCompare($1) == .orderedAscending})
Example:
var dict : [String : Any] = ["10" : Any, "2" : Any, "20" : Any, "1" : Any]
dictionary.keys.sorted()
["1" : Any, "10" : Any, "2" : Any, "20" : Any]
dictionary.keys.sorted(by: {$0.localizedStandardCompare($1) == .orderedAscending})
["1" : Any, "2" : Any, "10" : Any, "20" : Any]
Swift 5
Input your dictionary that you want to sort alphabetically by keys.
// Sort inputted dictionary with keys alphabetically.
func sortWithKeys(_ dict: [String: Any]) -> [String: Any] {
let sorted = dict.sorted(by: { $0.key < $1.key })
var newDict: [String: Any] = [:]
for sortedDict in sorted {
newDict[sortedDict.key] = sortedDict.value
}
return newDict
}
dict.sorted(by: { $0.key < $1.key }) by it self returns a tuple (value, value) instead of a dictionary [value: value]. Thus, the for loop parses the tuple to return as a dictionary. That way, you put in a dictionary & get a dictionary back.
For Swift 4 the following has worked for me:
let dicNumArray = ["q":[1,2,3,4,5],"a":[2,3,4,5,5],"s":[123,123,132,43,4],"t":[00,88,66,542,321]]
let sortedDic = dicNumArray.sorted { (aDic, bDic) -> Bool in
return aDic.key < bDic.key
}
This is an elegant alternative to sorting the dictionary itself:
As of Swift 4 & 5
let sortedKeys = myDict.keys.sorted()
for key in sortedKeys {
// Ordered iteration over the dictionary
let val = myDict[key]
}
"sorted" in iOS 9 & xcode 7.3, swift 2.2 is impossible, change "sorted" to "sort", like this:
let dictionary = ["main course": 10.99, "dessert": 2.99, "salad": 5.99]
let sortedKeysAndValues = Array(dictionary).sort({ $0.0 < $1.0 })
print(sortedKeysAndValues)
//sortedKeysAndValues = ["desert": 2.99, "main course": 10.99, "salad": 5.99]
For Swift 3, the following sort returnes sorted dictionary by keys:
let unsortedDictionary = ["4": "four", "2": "two", "1": "one", "3": "three"]
let sortedDictionary = unsortedDictionary.sorted(by: { $0.0.key < $0.1.key })
print(sortedDictionary)
// ["1": "one", "2": "two", "3": "three", "4": "four"]
For Swift 3 the following has worked for me and the Swift 2 syntax has not worked:
// menu is a dictionary in this example
var menu = ["main course": 10.99, "dessert": 2.99, "salad": 5.99]
let sortedDict = menu.sorted(by: <)
// without "by:" it does not work in Swift 3
Swift 3 is sorted(by:<)
let dictionary = [
"A" : [1, 2],
"Z" : [3, 4],
"D" : [5, 6]
]
let sortedKeys = Array(dictionary.keys).sorted(by:<) // ["A", "D", "Z"]
Swift Sort Dictionary by keys
Since Dictionary[About] is based on hash function it is not possible to use a sort function as we are used to(like Array). As an alternative you are able to implement a kind of Java TreeMap/TreeSet
Easiest way is to use .sorted(by:) function. It returns an sorted Array of key/value tupples which is easy to handle
Example:
struct Person {
let name: String
let age: Int
}
struct Section {
let header: String
let rows: [Int]
}
let persons = [
Person(name: "A", age: 1),
Person(name: "B", age: 1),
Person(name: "C", age: 1),
Person(name: "A", age: 2),
Person(name: "B", age: 2),
Person(name: "C", age: 2),
Person(name: "A", age: 3),
Person(name: "B", age: 3),
Person(name: "C", age: 3),
]
//grouped
let personsGroupedByName: [String : [Person]] = Dictionary(grouping: persons, by: { $0.name })
/**
personsGroupedByName
[0] = {
key = "B"
value = 3 values {
[0] = (name = "B", age = 1)
[1] = (name = "B", age = 2)
[2] = (name = "B", age = 3)
}
}
[1] = {
key = "A"
value = 3 values {
[0] = (name = "A", age = 1)
[1] = (name = "A", age = 2)
[2] = (name = "A", age = 3)
}
}
[2] = {
key = "C"
value = 3 values {
[0] = (name = "C", age = 1)
[1] = (name = "C", age = 2)
[2] = (name = "C", age = 3)
}
}
*/
//sort by key
let sortedPersonsGroupedByName: [Dictionary<String, [Person]>.Element] = personsGroupedByName.sorted(by: { $0.0 < $1.0 })
/**
sortedPersonsGroupedByName
[0] = {
key = "A"
value = 3 values {
[0] = (name = "A", age = 1)
[1] = (name = "A", age = 2)
[2] = (name = "A", age = 3)
}
}
[1] = {
key = "B"
value = 3 values {
[0] = (name = "B", age = 1)
[1] = (name = "B", age = 2)
[2] = (name = "B", age = 3)
}
}
[2] = {
key = "C"
value = 3 values {
[0] = (name = "C", age = 1)
[1] = (name = "C", age = 2)
[2] = (name = "C", age = 3)
}
}
*/
//handle
let sections: [Section] = sortedPersonsGroupedByName.compactMap { (key: String, value: [Person]) -> Section in
let rows = value.map { person -> Int in
return person.age
}
return Section(header: key, rows: rows)
}
/**
sections
[0] = {
header = "A"
rows = 3 values {
[0] = 1
[1] = 2
[2] = 3
}
}
[1] = {
header = "B"
rows = 3 values {
[0] = 1
[1] = 2
[2] = 3
}
}
[2] = {
header = "C"
rows = 3 values {
[0] = 1
[1] = 2
[2] = 3
}
}
*/
my two cents, as all answers seem to miss we have a Dict and we do want a Dict:
var menu = ["main course": 10.99, "dessert": 2.99, "salad": 5.99]
let sortedMenu = menu.sorted(by: <) // is an ARRAY
print(type(of: sortedMenu))
let sortedDict = Dictionary( uniqueKeysWithValues: menu.sorted(by: <) )
print(type(of: sortedDict))
Above I sorted by value, by Key:
let sorted1 = menu.sorted { (kv1, kv2) in
return kv1.key < kv2.key
}
and/or apply conversion to Dict using constructor.

Swift: How to set a value in a Dictionary at a given sublevel?

Given this dictionary as an example:
var d = ["a": ["b": ["c": 30, "d": 20] ] ]
I would like to write a function that could add/update a value into this dictionary.
First example, if I call :
d.set(40, at: ["a", "b", "c"])
The function would progressively go into key a, then b, then c, and transform this dictionary into:
var d = ["a": ["b": ["c": 40, "d": 20] ] ]
Now if I call this instead:
d.set(60, at: ["a", "e", "f"])
I would like to get this dictionary instead:
var d = ["a": ["b": ["c": 40, "d": 20] ], "e": ["f": 60] ]
Could you help me writing this function?
Thank you for your help
Your example only requires this. Is your question actually larger than that? If so, please amend!
extension Dictionary {
subscript<Value>(_ key0: Key, _ key1: Key, _ key2: Key) -> Value?
where Self.Value == [Key: [Key: Value]] {
get { self[key0]?[key1]?[key2] }
set { self[key0, default: [:]][key1, default: [:]][key2] = newValue }
}
}
d["a", "b", "c"] // 30
d["a", "b", "c"] = 40
d["a", "e", "f"] // nil
d["a", "e", "f"] = 60
This seems to do the trick:
Edit Now this covers all the provided uses cases
extension Dictionary where Value == Any {
mutating func set<V>(_ value: V, at keyPath: [Key]) {
var keyPath = keyPath
guard !keyPath.isEmpty else { return }
let topLevel = keyPath.removeFirst()
guard !keyPath.isEmpty else {
self[topLevel] = value
return
}
guard keyPath.count > 1 else {
self = merge(with: [topLevel: [keyPath.last!: value]])
return
}
let leafKey = keyPath.removeLast()
let nested = [leafKey: value].nest(in: keyPath)
guard let child = self[topLevel] as? [Key: Any] else {
self[topLevel] = nested
return
}
self = self.merge(with: [topLevel: child.merge(with: nested)])
}
func merge(with other: [Key: Any] ) -> [Key: Any] {
var result = self
for key in other.keys {
if !keys.contains(key) {
result[key] = other[key]
} else {
switch (self[key], other[key]) {
case let (lhs, rhs) as ([Key: Any], [Key: Any]):
result[key] = lhs.merge(with: rhs)
default:
result[key] = other[key]
}
}
}
return result
}
func nest(in keys: [Key]) -> [Key: Any] {
let initial: [Key: Any] = self as [Key: Any]
let result: [Key: Any] = keys.reversed().reduce(into: initial) { result, key in
result = [key: result]
}
return result
}
}
var d: [String: Any] = ["a": ["b": ["c": 30, "d": 20] ] ] // ["a": ["b": ["c": 30, "d": 20]]]
d.set(40, at: ["a", "b", "c"]) // ["a": ["b": ["c": 40, "d": 20]]]
d.set(60, at: ["a", "e", "f"]) // ["a": ["b": ["c": 40, "d": 20], "e": ["f": 60]]]
d.set(["f": 42], at: ["a", "d"]) // ["a": ["b": ["c": 40, "d": 20], "e": ["f": 60], "d": ["f": 42]]]

Remove last elements of a collection while a condition in Swift

I am attempting to remove "" & " " from the back of a string array until the last item contains some text, but my implementation isn't picking up " ".
My implementation so far:
var array = ["A", "B", "", "C", "D", " ", " ", ""]
while true {
if (array.last == " " || array.last == "") {
array.removeLast()
} else {
break
}
}
My desired output is
["A", "B", "", "C", "D"]
, but my current output is
["A", "B", "", "C", "D", " ", " "]
, where the while loop simply breaks after encountering " "
Any advice why is it not picking up the " "?
I don't know why they have drop(while:) and did not implement dropLast(while:). The implementation bellow works on any collection:
extension Collection {
func dropLast(while predicate: (Element) throws -> Bool) rethrows -> SubSequence {
guard let index = try indices.reversed().first(where: { try !predicate(self[$0]) }) else {
return self[startIndex..<startIndex]
}
return self[...index]
}
}
"123".dropLast(while: \.isWholeNumber) // ""
"abc123".dropLast(while: \.isWholeNumber) // "abc"
"123abc".dropLast(while: \.isWholeNumber) // "123abc"
And extending RangeReplaceableCollection we can implement remove(while:) and removeLast(while:) as well:
extension RangeReplaceableCollection {
mutating func remove(while predicate: (Element) throws -> Bool) rethrows {
guard let index = try indices.first(where: { try !predicate(self[$0]) }) else {
removeAll()
return
}
removeSubrange(..<index)
}
mutating func removeLast(while predicate: (Element) throws -> Bool) rethrows {
guard let index = try indices.reversed().first(where: { try !predicate(self[$0]) }) else {
removeAll()
return
}
removeSubrange(self.index(after: index)...)
}
}
var string = "abc123"
string.removeLast(while: \.isWholeNumber)
string // "abc"
var string2 = "abc123"
string2.remove(while: \.isLetter)
string2 // "123"
var array = ["A", "B", "", "C", "D", " ", " ", ""]
array.removeLast { $0 == "" || $0 == " " }
array // ["A", "B", "", "C", "D"]
One way to solve this is to reverse the collection (which is done lazily) and drop the unwanted items until you encounter the wanted ones. Afterwards, reverse the collection again.
let array = ["A", "B", "", "C", "D", " ", " ", ""]
let filtered = array.reversed().drop(while: {
$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}).reversed() as [String]
print(filtered) // "["A", "B", "", "C", "D"]\n"
Note that the check for " " may fail if it's not a normal space, for example a non-breaking space (Unicode checkpoint U+00A0). This may be the issue you're having in the first place. So trim the string (it removes characters from the start and end only) and check whether the result is an empty string.
Move your condition to while and make sure you're checking on the correct array after the operation.
var array = ["A", "B", "", "C", "D", " ", " ", ""]
while array.last == " " || array.last == "" {
array.removeLast()
}
print(array) // ["A", "B", "", "C", "D"]
Just for fun, lets extend Array with this functionality in a generic way while also externally providing the condition for more flexibility.
Similar to Arrays having a drop(while:), we can make a dropLast(while:) like so:
extension Array {
func dropLast(while handler: (Element)->Bool) -> Array {
var array = self
while let last = array.last, handler(last) {
array.removeLast()
}
return array
}
}
Usage Example:
let array = ["", "A", "B", "", "C", "D", " ", " ", ""]
let modified = array.dropLast { $0.trimmingCharacters(in: .whitespaces).isEmpty }
print(modified) //["", "A", "B", "", "C", "D"]
Bonus:
It can handle other types of arrays too, and since the condition is not baked into the functionality, it's flexible and reusable.
let array = [0, 1, 2, 3, 0, 5, 6, 7, 0, -1, 0, -2]
//Drop (from tail) all numbers less than 1
let modified = array.dropLast(while: { (val) -> Bool in
return val < 1
})
print(modified) //[0, 1, 2, 3, 0, 5, 6, 7]
Basically your solution plays fine. But you can make it more generic for "", " ", "", " ", .... :
import Foundation
var array = ["A", "B", "", "C", "D", " ", " ", ""]
while true {
let shouldRemoveLast = array.last?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? false
if (shouldRemoveLast) { array.removeLast() } else { break }
}
For completeness, here's a one liner, functional programming style, extension, that removes the last elements that satisfy the given criteria:
extension Collection {
func dropLast(while predicate: (Element) -> Bool) -> SubSequence {
indices.reversed().first { !predicate(self[$0]) }.map(prefix(through:)) ?? prefix(0)
}
}
Same code, but in a more imperative style:
extension Collection {
func dropLast(while predicate: (Element) -> Bool) -> SubSequence {
if let lastValidIndex = indices.reversed().first(where: { !predicate(self[$0]) }) {
return prefix(through: lastValidIndex)
} else {
return prefix(0)
}
}
}

How to deep merge 2 Swift dictionaries

My question is simple, I want to know how to do a deep merge of 2 Swift dictionaries (not NSDictionary).
let dict1 = [
"a": 1,
"b": 2,
"c": [
"d": 3
],
"f": 2
]
let dict2 = [
"b": 4,
"c": [
"e": 5
],
"f": ["g": 6]
]
let dict3 = dict1.merge(dict2)
/* Expected:
dict3 = [
"a": 1,
"b": 4,
"c": [
"d": 3,
"e": 5
],
"f": ["g": 6]
]
*/
When dict1 and dict2 have the same key, I expect the value to be replaced, but if that value is another dictionary, I expect it to be merged recursively.
Here is the solution I'd like:
protocol Mergeable {
mutating func merge(obj: Self)
}
extension Dictionary: Mergeable {
// if they have the same key, the new value is taken
mutating func merge(dictionary: Dictionary) {
for (key, value) in dictionary {
let oldValue = self[key]
if oldValue is Mergeable && value is Mergeable {
var oldValue = oldValue as! Mergeable
let newValue = value as! Mergeable
oldValue.merge(newValue)
self[key] = oldValue
} else {
self[key] = value
}
}
}
}
but it gives me the error Protocol 'Mergeable' can only be used as a generic constraint because it has Self or associated type requirements
EDIT:
My question is different from Swift: how to combine two Dictionary instances? because that one is not a deep merge.
With that solution, it would produce:
dict3 = [
"a": 1,
"b": 4,
"c": [
"e": 5
]
]
In my view the question is incoherent. This is an answer, however:
func deepMerge(d1:[String:AnyObject], _ d2:[String:AnyObject]) -> [String:AnyObject] {
var result = [String:AnyObject]()
for (k1,v1) in d1 {
result[k1] = v1
}
for (k2,v2) in d2 {
if v2 is [String:AnyObject], let v1 = result[k2] where v1 is [String:AnyObject] {
result[k2] = deepMerge(v1 as! [String:AnyObject],v2 as! [String:AnyObject])
} else {
result[k2] = v2
}
}
return result
}
Here is your test case:
let dict1:[String:AnyObject] = [
"a": 1,
"b": 2,
"c": [
"d": 3
]
]
let dict2:[String:AnyObject] = [
"b": 4,
"c": [
"e": 5
]
]
let result = deepMerge(dict1, dict2)
NSLog("%#", result)
/*
{
a = 1;
b = 4;
c = {
d = 3;
e = 5;
};
}
*/
3rd-party edit: Alternate version using variable binding and newer Swift syntax.
func deepMerge(_ d1: [String: Any], _ d2: [String: Any]) -> [String: Any] {
var result = d1
for (k2, v2) in d2 {
if let v1 = result[k2] as? [String: Any], let v2 = v2 as? [String: Any] {
result[k2] = deepMerge(v1, v2)
} else {
result[k2] = v2
}
}
return result
}
How about manually doing it.
func += <KeyType, ValueType> (inout left: Dictionary<KeyType, ValueType>, right: Dictionary<KeyType, ValueType>) {
for (k, v) in right {
left.updateValue(v, forKey: k)
}
}
You can also try ExSwift Library
extension Dictionary {
mutating func deepMerge(_ dict: Dictionary) {
merge(dict) { (current, new) in
if var currentDict = current as? Dictionary, let newDict = new as? Dictionary {
currentDict.deepMerge(newDict)
return currentDict as! Value
}
return new
}
}
}
How to use/test:
var dict1: [String: Any] = [
"a": 1,
"b": 2,
"c": [
"d": 3
],
"f": 2
]
var dict2: [String: Any] = [
"b": 4,
"c": [
"e": 5
],
"f": ["g": 6]
]
dict1.deepMerge(dict2)
print(dict1)

Sort Dictionary by keys

I want to sort a dictionary in Swift. I have a dictionary like:
"A" => Array[]
"Z" => Array[]
"D" => Array[]
etc. I want it to be like
"A" => Array[]
"D" => Array[]
"Z" => Array[]
etc.
I have tried many solutions on SO but no one worked for me. I am using XCode6 Beta 5 and on it some are giving compiler error and some solutions are giving exceptions. So anyone who can post the working copy of dictionary sorting.
let dictionary = [
"A" : [1, 2],
"Z" : [3, 4],
"D" : [5, 6]
]
let sortedKeys = Array(dictionary.keys).sorted(<) // ["A", "D", "Z"]
EDIT:
The sorted array from the above code contains keys only, while values have to be retrieved from the original dictionary. However, 'Dictionary' is also a 'CollectionType' of (key, value) pairs and we can use the global 'sorted' function to get a sorted array containg both keys and values, like this:
let sortedKeysAndValues = sorted(dictionary) { $0.0 < $1.0 }
println(sortedKeysAndValues) // [(A, [1, 2]), (D, [5, 6]), (Z, [3, 4])]
EDIT2: The monthly changing Swift syntax currently prefers
let sortedKeys = Array(dictionary.keys).sort(<) // ["A", "D", "Z"]
The global sorted is deprecated.
To be clear, you cannot sort Dictionaries. But you can out put an array, which is sortable.
Swift 2.0
Updated version of Ivica M's answer:
let wordDict = [
"A" : [1, 2],
"Z" : [3, 4],
"D" : [5, 6]
]
let sortedDict = wordDict.sort { $0.0 < $1.0 }
print("\(sortedDict)") //
Swift 3
wordDict.sorted(by: { $0.0 < $1.0 })
In Swift 5, in order to sort Dictionary by KEYS
let sortedYourArray = YOURDICTIONARY.sorted( by: { $0.0 < $1.0 })
In order to sort Dictionary by VALUES
let sortedYourArray = YOURDICTIONARY.sorted( by: { $0.1 < $1.1 })
If you want to iterate over both the keys and the values in a key sorted order, this form is quite succinct
let d = [
"A" : [1, 2],
"Z" : [3, 4],
"D" : [5, 6]
]
Swift 1,2:
for (k,v) in Array(d).sorted({$0.0 < $1.0}) {
println("\(k):\(v)")
}
Swift 3+:
for (k,v) in Array(d).sorted(by: {$0.0 < $1.0}) {
println("\(k):\(v)")
}
I tried all of the above, in a nutshell all you need is
let sorted = dictionary.sorted { $0.key < $1.key }
let keysArraySorted = Array(sorted.map({ $0.key }))
let valuesArraySorted = Array(sorted.map({ $0.value }))
In swift 4 you can write it smarter:
let d = [ 1 : "hello", 2 : "bye", -1 : "foo" ]
d = [Int : String](uniqueKeysWithValues: d.sorted{ $0.key < $1.key })
Swift 4 & 5
For string keys sorting:
dictionary.keys.sorted(by: {$0.localizedStandardCompare($1) == .orderedAscending})
Example:
var dict : [String : Any] = ["10" : Any, "2" : Any, "20" : Any, "1" : Any]
dictionary.keys.sorted()
["1" : Any, "10" : Any, "2" : Any, "20" : Any]
dictionary.keys.sorted(by: {$0.localizedStandardCompare($1) == .orderedAscending})
["1" : Any, "2" : Any, "10" : Any, "20" : Any]
Swift 5
Input your dictionary that you want to sort alphabetically by keys.
// Sort inputted dictionary with keys alphabetically.
func sortWithKeys(_ dict: [String: Any]) -> [String: Any] {
let sorted = dict.sorted(by: { $0.key < $1.key })
var newDict: [String: Any] = [:]
for sortedDict in sorted {
newDict[sortedDict.key] = sortedDict.value
}
return newDict
}
dict.sorted(by: { $0.key < $1.key }) by it self returns a tuple (value, value) instead of a dictionary [value: value]. Thus, the for loop parses the tuple to return as a dictionary. That way, you put in a dictionary & get a dictionary back.
For Swift 4 the following has worked for me:
let dicNumArray = ["q":[1,2,3,4,5],"a":[2,3,4,5,5],"s":[123,123,132,43,4],"t":[00,88,66,542,321]]
let sortedDic = dicNumArray.sorted { (aDic, bDic) -> Bool in
return aDic.key < bDic.key
}
This is an elegant alternative to sorting the dictionary itself:
As of Swift 4 & 5
let sortedKeys = myDict.keys.sorted()
for key in sortedKeys {
// Ordered iteration over the dictionary
let val = myDict[key]
}
"sorted" in iOS 9 & xcode 7.3, swift 2.2 is impossible, change "sorted" to "sort", like this:
let dictionary = ["main course": 10.99, "dessert": 2.99, "salad": 5.99]
let sortedKeysAndValues = Array(dictionary).sort({ $0.0 < $1.0 })
print(sortedKeysAndValues)
//sortedKeysAndValues = ["desert": 2.99, "main course": 10.99, "salad": 5.99]
For Swift 3, the following sort returnes sorted dictionary by keys:
let unsortedDictionary = ["4": "four", "2": "two", "1": "one", "3": "three"]
let sortedDictionary = unsortedDictionary.sorted(by: { $0.0.key < $0.1.key })
print(sortedDictionary)
// ["1": "one", "2": "two", "3": "three", "4": "four"]
For Swift 3 the following has worked for me and the Swift 2 syntax has not worked:
// menu is a dictionary in this example
var menu = ["main course": 10.99, "dessert": 2.99, "salad": 5.99]
let sortedDict = menu.sorted(by: <)
// without "by:" it does not work in Swift 3
Swift 3 is sorted(by:<)
let dictionary = [
"A" : [1, 2],
"Z" : [3, 4],
"D" : [5, 6]
]
let sortedKeys = Array(dictionary.keys).sorted(by:<) // ["A", "D", "Z"]
Swift Sort Dictionary by keys
Since Dictionary[About] is based on hash function it is not possible to use a sort function as we are used to(like Array). As an alternative you are able to implement a kind of Java TreeMap/TreeSet
Easiest way is to use .sorted(by:) function. It returns an sorted Array of key/value tupples which is easy to handle
Example:
struct Person {
let name: String
let age: Int
}
struct Section {
let header: String
let rows: [Int]
}
let persons = [
Person(name: "A", age: 1),
Person(name: "B", age: 1),
Person(name: "C", age: 1),
Person(name: "A", age: 2),
Person(name: "B", age: 2),
Person(name: "C", age: 2),
Person(name: "A", age: 3),
Person(name: "B", age: 3),
Person(name: "C", age: 3),
]
//grouped
let personsGroupedByName: [String : [Person]] = Dictionary(grouping: persons, by: { $0.name })
/**
personsGroupedByName
[0] = {
key = "B"
value = 3 values {
[0] = (name = "B", age = 1)
[1] = (name = "B", age = 2)
[2] = (name = "B", age = 3)
}
}
[1] = {
key = "A"
value = 3 values {
[0] = (name = "A", age = 1)
[1] = (name = "A", age = 2)
[2] = (name = "A", age = 3)
}
}
[2] = {
key = "C"
value = 3 values {
[0] = (name = "C", age = 1)
[1] = (name = "C", age = 2)
[2] = (name = "C", age = 3)
}
}
*/
//sort by key
let sortedPersonsGroupedByName: [Dictionary<String, [Person]>.Element] = personsGroupedByName.sorted(by: { $0.0 < $1.0 })
/**
sortedPersonsGroupedByName
[0] = {
key = "A"
value = 3 values {
[0] = (name = "A", age = 1)
[1] = (name = "A", age = 2)
[2] = (name = "A", age = 3)
}
}
[1] = {
key = "B"
value = 3 values {
[0] = (name = "B", age = 1)
[1] = (name = "B", age = 2)
[2] = (name = "B", age = 3)
}
}
[2] = {
key = "C"
value = 3 values {
[0] = (name = "C", age = 1)
[1] = (name = "C", age = 2)
[2] = (name = "C", age = 3)
}
}
*/
//handle
let sections: [Section] = sortedPersonsGroupedByName.compactMap { (key: String, value: [Person]) -> Section in
let rows = value.map { person -> Int in
return person.age
}
return Section(header: key, rows: rows)
}
/**
sections
[0] = {
header = "A"
rows = 3 values {
[0] = 1
[1] = 2
[2] = 3
}
}
[1] = {
header = "B"
rows = 3 values {
[0] = 1
[1] = 2
[2] = 3
}
}
[2] = {
header = "C"
rows = 3 values {
[0] = 1
[1] = 2
[2] = 3
}
}
*/
my two cents, as all answers seem to miss we have a Dict and we do want a Dict:
var menu = ["main course": 10.99, "dessert": 2.99, "salad": 5.99]
let sortedMenu = menu.sorted(by: <) // is an ARRAY
print(type(of: sortedMenu))
let sortedDict = Dictionary( uniqueKeysWithValues: menu.sorted(by: <) )
print(type(of: sortedDict))
Above I sorted by value, by Key:
let sorted1 = menu.sorted { (kv1, kv2) in
return kv1.key < kv2.key
}
and/or apply conversion to Dict using constructor.