Swift 2 - Separating an Array into a Dictionary with Keys from a to Z

Swift 2 - Separating an array into a dictionary with keys from A to Z

Using only Swift 2 objects and methods, and with a key for each letter in the alphabet:

let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters.map({ String($0) })

let words = ["Apple", "Banana", "Blueberry", "Eggplant"]

var result = [String:[String]]()

for letter in alphabet {
result[letter] = []
let matches = words.filter({ $0.hasPrefix(letter) })
if !matches.isEmpty {
for word in matches {
result[letter]?.append(word)
}
}
}

print(result)

Swift - sorting and separating array of dictionaries

Here's a simple one-liner approach that might get you started:

let d = Dictionary.init(grouping: array) {$0["groupID"]!}

The result is a dictionary keyed by the value of the groupIDs:

["1": [["groupID": "1", "value": "3"], ["groupID": "1", "value": "2"]],
"2": [["groupID": "2", "value": "5"], ["groupID": "2", "value": "1"]],
"3": [["groupID": "3", "value": "6"], ["groupID": "3", "value": "9"]]]

Well, think about that result. The values of the dictionary are your three "desired output" arrays:

[["value":"2", "groupID":"1"],
["value":"3", "groupID":"1"]]

[["value":"1", "groupID":"2"],
["value":"5", "groupID":"2"]]

[["value":"6", "groupID":"3"],
["value":"9", "groupID":"3"]]

You can access the values as the values of the dictionary, or you can use the keys to sort by key and then dive in for each array.

Deriving the single sorted array is trivial.

Is there a declarative way to transform Array to Dictionary?

Swift 4

As alluded to by fl034, this can be simplified some with Swift 4 where an error checked version looks like:

let foo = entries
.map { $0.components(separatedBy: "=") }
.reduce(into: [String:Int64]()) { dict, pair in
if pair.count == 2, let value = Int64(pair[1]) {
dict[pair[0]] = value
}
}

Even simpler if you don't want the values as Ints:

let foo = entries
.map { $0.components(separatedBy: "=") }
.reduce(into: [String:String]()) { dict, pair in
if pair.count == 2 {
dict[pair[0]] = pair[1]
}
}

Older TL;DR

Minus error checking, it looks pretty much like:

let foo = entries.map({ $0.componentsSeparatedByString("=") })
.reduce([String:Int]()) { acc, comps in
var ret = acc
ret[comps[0]] = Int(comps[1])
return ret
}

Use map to turn the [String] into a split up [[String]] and then build the dictionary of [String:Int] from that using reduce.

Or, by adding an extension to Dictionary:

extension Dictionary {
init(elements:[(Key, Value)]) {
self.init()
for (key, value) in elements {
updateValue(value, forKey: key)
}
}
}

(Quite a useful extension btw, you can use it for a lot of map/filter operations on Dictionaries, really kind of a shame it doesn't exist by default)

It becomes even simpler:

let dict = Dictionary(elements: entries
.map({ $0.componentsSeparatedByString("=") })
.map({ ($0[0], Int($0[1])!)})
)

Of course, you can also combine the two map calls, but I prefer to break up the individual transforms.

If you want to add some error checking, flatMap can be used instead of map:

let dict2 = [String:Int](elements: entries
.map({ $0.componentsSeparatedByString("=") })
.flatMap({
if $0.count == 2, let value = Int($0[1]) {
return ($0[0], value)
} else {
return nil
}})
)

Again, if you want, you can obviously merge the map into the flatMap or split them for simplicity.

let dict2 = [String:Int](elements: entries.flatMap {
let parts = $0.componentsSeparatedByString("=")
if parts.count == 2, let value = Int(parts[1]) {
return (parts[0], value)
} else {
return nil
}}
)

Optionals in Dictionary Arrays

You are using Any as the type for the value of your output dictionary, while it can just be an array of Strings. And using Array(arrayLiteral: dictionaryOfDatasets[currentKeyValue]) does not help.

You can obtain the result you want by iterating through the original dictionary and storing the values directly into the new one, with no supporting function like returnKeyofDictionaryArray.

Here is how you can achieve this:

        //  The variable defintions including the expected unique key values, the associated data and the dictionary which will be returned
var arrayOfUniqueFieldNames = ["value1", "value2", "value3"]
var dictionaryOfKeyPairedData : [Int: [String]] = [
0: ["value1", "a"],
1: ["value2", "b"],
2: ["value3", "c"],
3: ["value1", "x"],
4: ["value2", "y"],
5: ["value3", "z"]]

// This is a dictionary of String to String-arrays
var dictionaryOfDatasets = [String: [String]]()

// MARK:- Code Body

// Just iterate through the values of the dictionary, given that
// you are not interested in the Int keys
dictionaryOfKeyPairedData.values.forEach {
if dictionaryOfDatasets[$0[0]] == nil {
dictionaryOfDatasets[$0[0]] = [$0[1]]
} else {
dictionaryOfDatasets[$0[0]]?.append($0[1])
}
}

// Sorting, if you need:
// Here is where you sort the arrays inside each value of the dictionary
dictionaryOfDatasets.keys.forEach {
dictionaryOfDatasets[$0] = dictionaryOfDatasets[$0]?.sorted(by: { $0 < $1 })
}

// prints the data - OUTPUT
print(dictionaryOfDatasets) // ["value3": ["c", "z"], "value2": ["b", "y"], "value1": ["a", "x"]]

How do you add a Dictionary of items into another Dictionary

You can define += operator for Dictionary, e.g.,

func += <K, V> (left: inout [K:V], right: [K:V]) { 
for (k, v) in right {
left[k] = v
}
}

Sort Dictionary by keys

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.

Swift: sort dictionary (by keys or by values) and return ordered array (of keys or values)

How's about functional programming style ?

typealias DictSorter = ((String,Int),(String,Int)) -> Bool

let alphaAtoZ: DictSorter = { $0.0 < $1.0 }
let alphaZtoA: DictSorter = { $0.0 > $1.0 }
let sizeSmallToLarge: DictSorter = { $0.1 < $1.1 }
let sizeLargeToSmall: DictSorter = { $0.1 > $1.1 }

// selector
let listSelector: (String,Int)->String = { $0.0 }
let countSelector: (String,Int)->Int = { $0.1 }

// Usage
let dict = ["Alpha" : 24, "Beta" : 47, "Gamma" : 12, "Delta" : 33]

let folderListByAlphaAtoZ = dict.sort(alphaAtoZ).map(listSelector)
let folderListByAlphaZtoA = dict.sort(alphaZtoA).map(listSelector)
let folderListBySizeSmallToLarge = dict.sort(sizeSmallToLarge).map(listSelector)
let folderListBySizeLargeToSmall = dict.sort(sizeLargeToSmall).map(listSelector)

let folderCountByAlphaAtoZ = dict.sort(alphaAtoZ).map(countSelector)
let folderCountByAlphaZtoA = dict.sort(alphaZtoA).map(countSelector)
let folderCountBySizeSmallToLarge = dict.sort(sizeSmallToLarge).map(countSelector)
let folderCountBySizeLargeToSmall = dict.sort(sizeLargeToSmall).map(countSelector)

Split and sort string array swift 3

Solution 1:

Posting my comment as answer with code. Check if this will work for you. You can use sections from first array printed below(store dict.keys.sorted() into some array) and use that as key for displaying cells in that section.

let abcNameObjectArray = ["Amy", "Bernadette", "Brian", "Chris", "Candice", ""]

let dict = abcNameObjectArray.reduce([String: [String]]()) { (key, value) -> [String: [String]] in
var key = key
if let first = value.characters.first {
let prefix = String(describing: first).lowercased()
var array = key[prefix]

if array == nil {
array = []
}
array!.append(value)
key[prefix] = array!.sorted()
}
return key
}

print(dict.keys.sorted())
print(dict)

Output:

["a", "b", "c"]

["b": ["Bernadette", "Brian"], "a": ["Amy"], "c": ["Candice", "Chris"]]

Solution 2:

Here is one more solution using array of arrays as suggested by rmaddy

let abcNameObjectArray = ["Amy", "Bernadette", "Brian", "Chris", "Candice", ""]
let unicodeScalarA = UnicodeScalar("a")!

var arrayOfArrays = abcNameObjectArray.reduce([[String]]()) { (output, value) -> [[String]] in
var output = output

if output.count < 26 {
output = (1..<27).map{ _ in return []}
}

if let first = value.characters.first {
let prefix = String(describing: first).lowercased()
let prefixIndex = Int(UnicodeScalar(prefix)!.value - unicodeScalarA.value)
var array = output[prefixIndex]
array.append(value)
output[prefixIndex] = array.sorted()
}
return output
}

arrayOfArrays = arrayOfArrays.filter { $0.count > 0}
print(arrayOfArrays)

Output:

[["Amy"], ["Bernadette", "Brian"], ["Chris", "Candice"]]

You can have number of sections as count of this array and each member will give number of cells required in that section.

Swift - Trim and map the first letter from a Array

This will give you your index array by using compactMap to remove nil objects and to trim and get the first character of the last name. This is then cast to a Set to remove duplicates and finally sorted where the closure makes sure "" is sorted last

let indexArray = Set(objectArray
.compactMap { $0?.lastName.trimmingCharacters(in: .whitespaces).prefix(1).uppercased()})
.sorted(by: {
if $0.isEmpty {
return false
}
if $1.isEmpty {
return true
}
return $0 < $1
})


Related Topics



Leave a reply



Submit