Merge arrays with condition

2020-04-10 03:42发布

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.

3条回答
We Are One
2楼-- · 2020-04-10 04:22

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.

查看更多
冷血范
3楼-- · 2020-04-10 04:24

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
}
查看更多
家丑人穷心不美
4楼-- · 2020-04-10 04:28

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

查看更多
登录 后发表回答