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 Bag
s.
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