Home > Enterprise >  Swift: Most efficient way to split array of objects into multiple arrays based on object property?
Swift: Most efficient way to split array of objects into multiple arrays based on object property?

Time:09-11

What's the most efficient way to split an array of objects into multiple arrays based on a property?

For example, say we have the following items:

var items = [
    Food(name: "Cupcake", foodType: .dessert),
    Food(name: "Banana", foodType: .fruit),
    Food(name: "Grandma's Soup", foodType: .custom("home-made"),
    Food(name: "Ice-cream", foodType: .dessert),
]

What's the best way to split it into the following arrays based on foodType:

[Food(name: "Cupcake", foodType: .dessert), Food(name: "Ice-cream", foodType: .dessert)]
[Food(name: "Banana", foodType: .fruit)],
[Food(name: "Grandma's Soup", foodType: .custom("home-made")]

In my example, foodType is an enum that looks like this:

enum FoodType {
    case dessert
    case fruit
    case custom(string)
    case vegetable
}

Normally I'd make my enum conform to CaseIterable and then loop over allCases, however, given that I'm using associated values, that's not possible.

My Approach:

var sections: [FoodType: [String]] = [:]
items.forEach {
    sections[$0.foodType] = (sections[$0.foodType] ?? [])   [$0.name]
}

CodePudding user response:

If the order of the groups doesn't matter, I would make FoodType conform to Hashable:

enum FoodType: Hashable {
    case dessert
    case fruit
    case custom(String)
    case vegetable
}

Then use the Dictionary.init(grouping:by:) initialiser to create a [FoodType:[Food]]:

Dictionary(grouping: items, by: \.foodType)

The values of this dictionary are the groups you want:

let groups = [_](Dictionary(grouping: items, by: \.foodType).values)

This preserves the order of the elements in each group, but the order of the groups themselves is not preserved.

If you want to preserve the order of the groups, you can do:

let groups = items.reduce(into: [[Food]]()) { acc, food in
    for i in acc.indices {
        if acc[i][0].foodType == food.foodType {
            acc[i].append(food)
            return
        }
    }
    acc.append([food])
}
  • Related