Group A Dictionary By Key Value

Im sure i cant be the only one to have an app needing this functionality, yet i cant come across and Library to help with it thus far.

I have an Dictionary such as


[["order": 31399, "builder": "Test", "area": "North"], ["order": 4244, "builder": "Test", "area": "North"]["order": 41499, "builder": "new", "area": "South"]]


Im wanting to group this data by Area in this case into a dictionary like so


[

["North": [["order": 31399, "builder": "Test", "area": "North"], ["order": 4244, "builder": "Test", "area": "North"]]],

["South": [["order": 41499, "builder": "new", "area": "South"]]]

]


Or maybe even 2 levels down, by area and then by builder in the area. In JS i have used D3 and this process is super simple with nesting, in swift its becoming a nightmare. Even if i can just get 1 level grouping i will be happy at this point. Any help is appreciated. Thanks

Accepted Answer

No methods for grouping cannot be found in the Standard Library. You might need to do it yourself.

let dicts: [[String: AnyObject]] = [["order": 31399, "builder": "Test", "area": "North"], ["order": 4244, "builder": "Test", "area": "North"],["order": 41499, "builder": "new", "area": "South"]]
func groupByArea(acc: [String: [[String: AnyObject]]], elt: [String: AnyObject]) -> [String: [[String: AnyObject]]] {
    var result = acc
    let key = (elt["area"] as? String) ?? "*unspecified*"
    if result[key] == nil {
        result[key] = []
    }
    result[key]!.append(elt)
    return result
}
let grouped = dicts.reduce([:], combine: groupByArea)

Already more than answered my question, but if i was to want to go one more level in, i would just loop into this loop?

For Example group by area and in each area by builder.


in JSON example somthing like

["South": [["new": [[array of matching items]]], ["test": [[array of matching items]]], "North": [["new": [[array of matching items]]], ["test": [[array of matching items]]]

You need another strategy for the second level.

Before getting into it, groupByArea and groupByBuilder would be very similar, so having a function generator seems be a good way.

typealias GroupingFunc = ([String: [[String: AnyObject]]], [String: AnyObject]) -> [String: [[String: AnyObject]]]
func groupBy(keyName: String) -> GroupingFunc {
    return {acc, elt in
        var result = acc
        let key = (elt[keyName] as? String) ?? "*unspecified*"
        if result[key] == nil {
            result[key] = []
        }
        result[key]!.append(elt)
        return result
    }
}
let groupedByArea = dicts.reduce([:], combine: groupBy("area")) //same as `dicts.reduce([:], combine: groupByArea)`

With this, you can write something like this:

let dicts: [[String: AnyObject]] = [["order": 31399, "builder": "Test", "area": "North"], ["order": 4244, "builder": "Test", "area": "South"],["order": 41499, "builder": "new", "area": "South"]]
let groupedByArea = dicts.reduce([:], combine: groupBy("area"))
var groupedByAreaAndBuilder: [String: [String: [[String: AnyObject]]]] = [:]
for (area, subarray) in groupedByArea {
    groupedByAreaAndBuilder[area] = subarray.reduce([:], combine: groupBy("builder"))
}
print(groupedByAreaAndBuilder) //->["South": ["Test": [["order": 4244, "builder": Test, "area": South]], "new": [["order": 41499, "builder": new, "area": South]]], "North": ["Test": [["order": 31399, "builder": Test, "area": North]]]]

If you want to group by arbitrary multiple keys, that would be a little more complicated.

You have no idea how much you have helped me, THANK YOU!

Group A Dictionary By Key Value
 
 
Q