Home > Mobile >  Collect data for duplicate ids into an array of custom type
Collect data for duplicate ids into an array of custom type

Time:12-08

I have a list of tuples, with repeated "ids":

var tuples: [(Int, String)] = [
    (1, "A"),
    (1, "B"),
    (1, "C"),
    (2, "X"),
    (2, "Y"),
    (1, "Z")
]

I'd like to collect them into an array of custom Bag type:

struct Bag {
    let id: Int
    var names: [String]
}

let desiredResult = [
    Bag(id: 1, names: ["A", "B", "C", "Z"]),
    Bag(id: 2, names: ["X", "Y"])
]

What is the most performant way of accomplishing this?

CodePudding user response:

In Swift 5, the easiest implementation would be to use the native Dictionary(grouping:by:), which groups the value like you want, and then use map to create your array of bags:

Dictionary(grouping: tuples) { $0.0 }
    .map { Bag(id: $0.key, names: $0.value.map(\.1)) }

Some simple explanations: Dictionary(grouping: tuples) { $0.0 } groups your tuples array using the first element (.0), which results in a dictionary like:

let tuplesGrouped: [Int: [(Int, String)]] = [
    1: [(1, "A"), (1, "B"), (1, "C"), (1, "Z")],
    2: [(2, "X"), (2, "Y")],
]

The later map acts on the type [(key: Int, value: [(Int, String)])], which is then used to create the array of Bags.

CodePudding user response:

A simple/naive implementation (which looks for an existing match to append against, otherwise it appends to the top array:

var desired = [Bag]()
for tuple in tuples {
    if let index = desired.firstIndex(where: { $0.id == tuple.0 }) {
        desired[index].names.append(tuple.1)
    } else {
        desired.append(Bag(id: tuple.0, names: [tuple.1]))
    }
}

CodePudding user response:

You can use the high-order function reduce for this to create a dictionary using the id as key and Bag as value and then extract the values

let bags = tuples.reduce(into: [:]) {
    $0[$1.0, default: Bag(id: $1.0, names: [])].names.append($1.1)
}.values
  • Related