I would like to merge two arrays with specific condition and update objects that they are containing.
First my struct that is in arrays:
struct Item {
var id:Int
var name:String
var value:Int
}
Second elements for the two arrays:
let fisrt = Item(id: 1, name: "Oleg", value: 3)
let second = Item(id: 2, name: "Olexander", value:5)
let fisrtInSecond = Item(id: 1, name: "Bogdan", value: 6)
let secondInSecond = Item(id: 2, name: "Max", value: 9)
Arrays:
var fisrtArray = [fisrt, second]
let secondArray = [fisrtInSecond, secondInSecond]
I woudl like to use zip
and map
functions of the collection to achive result. Result is that fisrtArray
elements names
are updated by id
.
Example: fisrtArray = [Item(id: 1, name: "Bogdan", value:3), Item(id: 2, name: "Max", value:5)]
I know how to do this via simple loops. But i am looking for more advanced usage of the functional programing is Swift.
My experiment:
fisrtArray = zip(fisrtArray, secondArray).map()
The main problem i do not know how to write condition in the map
function. Condition should be:
if ($0.id == $1.id) {
$0.name = $1.name
}
From the comment discussing it is possible to highlight that zip
is not suitable in my case because we should iterate over all array to find if we have similar id's that are not in the same order.
The following code does work independently by the order of the elements inside the 2 arrays
firstArray = firstArray.map { (item) -> Item in
guard
let index = secondArray.index(where: { $0.id == item.id })
else { return item }
var item = item
item.name = secondArray[index].name
return item
}
"[Item(id: 1, name: "Bogdan", value: 3), Item(id: 2, name: "Max", value: 5)]\n"
Update
The following version uses the first(where:
method as suggested by Martin R.
firstArray = firstArray.map { item -> Item in
guard let secondElm = secondArray.first(where: { $0.id == item.id }) else { return item }
var item = item
item.name = secondElm.name
return item
}
A solution for your specific problem above would be:
struct Item {
var id: Int
var name: String
}
let first = Item(id: 1, name: "Oleg")
let second = Item(id: 2, name: "Olexander")
let firstInSecond = Item(id: 1, name: "Bogdan")
let secondInSecond = Item(id: 2, name: "Max")
let ret = zip([first, second], [firstInSecond, secondInSecond]).map({
return $0.id == $1.id ? $1 : $0
})
=> But it requires that there are as many items in the first as in the second array - and that they have both the same ids in the same order...
The map
function cannot directly mutate its elements. And since you're using structs (passed by value), it wouldn't work anyway, because the version you see in $0 would be a different instance than the one in the array. To use map
correctly, I'd use a closure like this:
fisrtArray = zip(fisrtArray, secondArray).map() {
return Item(id: $0.id, name: $1.name, value: $0.value)
}
This produces the result you're expecting.
Now, if your structs were objects (value types instead of reference types), you could use forEach
and do the $0.name = $1.name
in there.