Last holiday, I had the opportunity to start on a new side-project. One of the techniques I touched on, was grouping an array of model objects into sections. Instead of writing my umpteenth for
-loop for this, I opted to create a generic function for it.
My first attempt resulted in an Array
extension group(by:)
;
["1", "1", "2", "3", "3", "1"].group(by: { return $0 } )
// [ ["1", "1", "1"], ["3", "3"], ["2"] ]
It worked, but I still would have to do any sorting of the sections myself. Also, I wouldn’t have any information about the section grouping. But, for a first attempt, it’s great! Ship it!
After ‘shipping it’ to the CocoaHeadsNL Slack for some critique, Tim Vermeulen came up with some improvements, which resulted in the following snippet.
A Sequence
extension, which returns a grouped dictionary. Much handier than the original array!
public extension Sequence {
typealias Element = Iterator.Element
/// Returns all elements in arrays grouped by the closure.
public func grouped<Key: Hashable>(by key: (Element)->(Key)) -> [Key : [Element]] {
var dict: [Key : [Element]] = [:]
for element in self {
let key = key(element)
var array = dict.removeValue(forKey: key) ?? []
array.append(element)
dict.updateValue(array, forKey: key)
}
return dict
}
}
["1", "1", "2", "3", "3", "1"].grouped(by: { return $0 } )
// { "1" : ["1", "1", "1"], "3" : ["3", "3"], "2" : ["2"] }
What did I gain?
- practice with generic functions
- info about the Sequence function
- renewed faith in shipping early and often
Thanks to the Tim and the other CocoaHeads for collaborating on this! If you think the snippet is useful – or have an improvement – send me tweet or a toot!