scaryguy scaryguy - 4 months ago 12
Swift Question

How to group a dictionary array depending on a unique property value?

This is what I have, an array of dictionary response I fetched from an API:

var response = [
["title": "First one", "code": "the_first", "category": "Entertainment category"],
["title": "Second one", "code": "the_second", "category": "Entertainment category"],
["title": "Third one", "code": "the_third", "category": "News category"],
["title": "Fourth one", "code": "the_fourth", "category": "News category"],
["title": "Fifth one", "code": "the_fifth", "category": "Children category"],
["title": "Sixth one", "code": "the_sixth", "category": "Children category"]
]


What I need to have is:

var channels_by_category = [
["title":"Entertainment category",
"channels":[
["title": "First one", "code": "the_first", "category": "Entertainment category"],
["title": "Second one", "code": "the_second", "category": "Entertainment category"]
]],

["title":"News category",
"channels":[
"title": "Third one", "code": "the_third", "category": "News category"],
["title": "Fourth one", "code": "the_fourth", "category": "News category"]
]]
],

["title":"Children category",
"channels":[
["title": "Fifth one", "code": "the_fifth", "category": "Children category"],
["title": "Sixth one", "code": "the_sixth", "category": "Children category"]
]]
]


In JavaScript world, I'd use lodash to get over this problem. However, I'm not experienced enough in Swift. I guess that it should be some kind of combination of
for
loop and
reduce
but I couldn't figure out how to do it.

How to achieve what I want to get?

Thank you

Answer

Since a good answer to your question has already been provided I am suggesting here a different solution

You should not use dictionaries for your data model. This solutions shows you how to use a struct.

Show

first of all lets define a struct to represent your data model

struct Show {
    let title: String
    let code: String
    let category: String

    init?(dict:[String:String]) {
        guard let
            title = dict["title"],
            code = dict["code"],
            category = dict["category"]
            else { return nil }
        self.title = title
        self.code = code
        self.category = category
    }
}

Organising your data

Next let's convert your raw data into a list of Show(s)

let shows = response.flatMap(Show.init)

Grouping

And finally let's group the list by category

let categories: [String:[Show]] = shows.reduce([String:[Show]]()) { (categories, show) -> [String:[Show]] in
    var categories = categories
    categories[show.category] = (categories[show.category] ?? []) + [show]
    return categories
}

Now categories has your category name as key and a list of Show(s) as value.

Update 1: Iterating over a list of Show(s)

if let shows = categories["News category"] {
    for show in shows {
        print(show.title)
    }
}

or

categories["News category"]?.forEach { show in
    print(show.title)
}

Update 2: iterating over the categories

for elm in categories {
    let category = elm.0
    let shows = elm.1

    print("Category \(category) has \(shows.count) shows")
    shows.forEach { show in
        print(" - \(show.title)")
    }
}

Result

Category News category has 2 shows
 - Third one
 - Fourth one
Category Entertainment category has 2 shows
 - First one
 - Second one
Category Children category has 2 shows
 - Fifth one
 - Sixth one
Comments