3
\$\begingroup\$

I'm new at generics, but I've written an array extension to group an array by array element into a two dimensional array:

extension Array {
 func group<U: Hashable>(by key: (Element) -> U) -> [[Element]] {
 //keeps track of what the integer index is per group item
 var indexKeys = [U : Int]()
 var grouped = [[Element]]()
 for element in self {
 let key = key(element)
 if let ind = indexKeys[key] {
 grouped[ind].append(element)
 }
 else {
 grouped.append([element])
 indexKeys[key] = grouped.count - 1
 }
 }
 return grouped
 }
}

For an array of this struct:

struct Thing {
 var category: String
 var name: String
}

I want to be able to group an array of things [Thing] into a two dimensional array [[Thing]] based on Thing.category.

I use the extension like this:

let things = [
 Thing(category: "A", name: "Apple"),
 Thing(category: "B", name: "Boy"),
 Thing(category: "A", name: "Alligator"),
 Thing(category: "B", name: "Ball"),
 Thing(category: "B", name: "Billboard")
]
let groupedThings = things.group { 0ドル.category }

This returns something like:

//groupedThings
[
 [
 Thing(category: "A", name: "Apple"),
 Thing(category: "A", name: "Alligator")
 ],
 [
 Thing(category: "B", name: "Boy"),
 Thing(category: "B", name: "Ball"),
 Thing(category: "B", name: "Billboard")
 ]
]

Please note that I'm not concerned about sort order.

How does my extension look? Since I'm fairly new at this, I'd like to know if I'm over-complicating it. Is there a way to write the extension more concisely? What about speed issues?

asked Nov 9, 2017 at 14:10
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

That looks like a clean implementation to me. One minor thing: I find it more natural to assign the new index before the group array is extended, so that you don't have to subtract one:

indexKeys[key] = grouped.count
grouped.append([element])

The Swift standard library defines many methods for the Sequence protocol and not for concrete sequences like Array, examples are map, filter, min/max, contains ...

You can do the same with your method to make it more universally applicable:

extension Sequence {
 // ... no other changes necessary ...
}

However: You are essentially reinventing the wheel. In Swift 4 there is a Dictionary(grouping:by) method which does almost what your code does, only that a dictionary is returned:

let groupedThings = Dictionary(grouping: things, by: { 0ドル.category } )
// [String : [Thing]]

The keys are the categories, and the values are arrays of corresponding things. With

Array(groupedThings.values)

you get a nested array which (apart from the order) is identical to what your method returns.

answered Nov 9, 2017 at 16:36
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.