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 String
s. 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
Displaying Strings in iOS Randomly Without Repeating Them
How to Update iOS 14 Widget Background Color from the App
Backgroundtimeremaining Returns (35791394 Mins)
Scaling Current Dot of Uipagecontrol and Keeping It Centered
Schedule Local Notification Every N Days (Timezone Safe)
Dyld: Library Not Loaded: @Rpath/Mydsk.Framework/Mydsk -> Swift iOS 8.0
Scaling Physics Bodies in Xcode Spritekit
Fbsdk (New Facebook Sdk 4.0) Implementation Is Not Working for Login with Facebook
Swiftui Present View Modally via Tabview
How to Get a Substring from a Specific Character to the End of the String in Swift 4
How to Make Watchkit Extension App and My iPhone App Share the Same Icloud Databases
Remove Separator Line of Single Row in Eureka
iOS - Setobject VS. Synchronize
Swift How to Add Background Color Just Around Text in a Label
Alamofire Multipart Upload Post Error in Swift