Bastek Bastek - 4 months ago 9
iOS Question

Filter array of objects and sum their properties in Swift

I would like to generate a shopping list from an array of products.
I have an entity "Product" in Core Data that has name (String) and amount (Int) as its properties. I am in the moment where I have an array of products with some duplicates, something like that:

var products : [Product] = [Apple, Egg, Orange, Apple, Orange, Banana, Egg, Egg]


How can I filter such array to get sums of amount of specific products? I would like to get as a result the list of products with their amounts like:
Apple: 4, Banana: 3, Egg: 7, Orange 2
. I know that I can make a Set from that Array to avoid duplicates, but I don't know how to sum products' amount before that.

JMI JMI
Answer
enum Product {
    case Apple
    case Egg
    case Orange
    case Banana
}

let products: [Product] = [.Apple, .Egg, .Orange, .Apple, .Orange, .Banana, .Egg, .Egg]

products.reduce([:]) { (map, product) -> [Product: Int] in
    var updatedMap = map
    if let value = map[product] {
        updatedMap[product] = value + 1
    }
    else {
        updatedMap[product] = 1
    }
    return updatedMap
} //[Orange: 2, Apple: 2, Egg: 3, Banana: 1]

same with strings:

let products: [String] = ["Apple", "Egg", "Orange", "Apple", "Orange", "Banana", "Egg", "Egg"]

products.reduce([:]) { (map, product) -> [String: Int] in
    var updatedMap = map
    if let value = map[product] {
        updatedMap[product] = value + 1
    }
    else {
        updatedMap[product] = 1
    }
    return updatedMap
} // ["Apple": 2, "Egg": 3, "Orange": 2, "Banana": 1]

Or by extension for all hashable sequence types:

extension SequenceType where Generator.Element: Hashable {
    func countElements() -> [Generator.Element : Int] {
        return reduce([:]) { (map, element) -> [Generator.Element : Int] in
            var updatedMap = map
            if let value = map[element] {
                updatedMap[element] = value + 1
            }
            else {
                updatedMap[element] = 1
            }
            return updatedMap
        }
    }
}

products.countElements() //["Apple": 2, "Egg": 3, "Orange": 2, "Banana": 1]