James James - 5 months ago 13
Swift Question

Swift matching elements within nested array of structs

I'm a Swift newbie and I'm trying to get to grips with the following data structure. I have an array (

categories
) of
category
structs. Each
category
struct contains an array of
business
structs stored as a value on the property
items
. I'm not sure how to represent this kind of thing but hopefully this pseudo-code makes it a little bit clearer:

categories: [category]
- category: Struct
.categoryId: Int
.items: [business]
- business: Struct
.busId: Int
- business: Struct
.busId: Int
- category: Struct
.categoryId: Int
.items: [business]
- business: Struct
.busId: Int
- business: Struct
.busId: Int


Given a
busId
I'm trying to return a matching
business
Struct and the
categoryId
in which it is contained. I've tried using FlatMap and Map but I'm going round in circles trying to unwrap and filter this data structure.

Any pointers/advice about approaches to take would be great.

Answer

Given a Business struct defined like this

struct Business {
    let busID: Int
}

A Category like this

struct Category {
    let categoryID: Int
    let business: [Business]
}

And a list of Category

let categories = [
    Category(categoryID: 0, business: [Business(busID:0)]),
    Category(categoryID: 1, business: [Business(busID:1), Business(busID:2)])
]

We can extract the categoryID and the Business having a given Int

func search(businessID: Int, categories: [Category]) -> (categoryID: Int, business:Business)? {
    let res = categories.reduce([Int:Business]()) { (res, category) ->  [Int:Business] in
        guard res.isEmpty else { return res }
        var res = res
        if let business = (category.business.filter { $0.busID == businessID }).first {
            res[category.categoryID] = business
        }
        return res
    }

    guard let categoryID = res.keys.first, business = res[categoryID] else  { return nil }
    return (categoryID, business)
}

Example

enter image description here

Update

This is a shorter version which does not use reduce

func search(businessID: Int, categories: [Category]) -> (categoryID: Int, business:Business)? {
    guard let
        category = (categories.filter { $0.business.contains { $0.busID == businessID } } ).first,
        business = (category.business.filter { $0.busID == businessID } ).first
    else { return nil }
    return (category.categoryID, business)
}