0

How can I merge two (first and second) arrays of structures based on a key in structure (name). While merging I need to replace the existing element of first array with second array element, if any value changed in that element.

struct Example: Codable {
    var name: String
    var dob: String
    var address: String
}

var first: [Example] = []
var second: [Example] = []

first.append(Example(name: "Arun", dob: "01-01-1994", address: "Tirupati"))
first.append(Example(name: "Balaji", dob: "01-01-1994", address: "Tirupati"))
first.append(Example(name: "Prasanth", dob: "01-01-1994", address: "Tirupati"))
    first.append(Example(name: "Satish", dob: "01-01-1994", address: "Tirupati"))


second.append(Example(name: "Arun", dob: "01-01-1994", address: "Kadapa"))
second.append(Example(name: "Balaji", dob: "01-01-1994", address: "Tirupati"))
second.append(Example(name: "Prasanth", dob: "01-01-1994", address: "Tirupati"))
second.append(Example(name: "Harsha", dob: "01-01-1994", address: "Tirupati"))

/*    let merged: [Example] = merge(first, second, with: name)
'merged' must contain four elements Arun, Balaji, Prasanth, Satish, Harsha, 
but Arun details are from 'second', because 'address' changed in second. So, I need element from 'second'*/

Please let me know the easy way, Thanks.

Harsha
  • 760
  • 1
  • 7
  • 21
  • Possible duplicate of [How do I concatenate or merge arrays in Swift?](https://stackoverflow.com/questions/25146382/how-do-i-concatenate-or-merge-arrays-in-swift) – ABeard89 Jun 19 '18 at 09:54
  • 3
    Yes, but concatenating is different from merging. Concatenating may have duplicates. – Harsha Jun 19 '18 at 09:59
  • Is the order important? Else maybe a `(NS)Set` with `Equatable` or something like that, or `NSOrderedSet`? https://stackoverflow.com/a/44228470/1801544 (after `Equatable`?) – Larme Jun 19 '18 at 10:07
  • Is it guaranteed that `second` contains all elements of `first`? Or is it possible that not all elements of `first` are present in `second`? With the example in your question, `merged` should simply be `second`. – Dávid Pásztor Jun 19 '18 at 10:07
  • you can simply just Set(second).union(first). but you need to conform to Hashable first though. – koropok Jun 19 '18 at 10:09
  • The part about duplicates was added after the initial question. – ABeard89 Jun 19 '18 at 10:10
  • Either way, this is an extremely common question with several answers upon a quick search. Please research before asking. – ABeard89 Jun 19 '18 at 10:11
  • @ABeard89 from the edit history it's clear that the question was about merging and not concatenating from the beginning – Dávid Pásztor Jun 19 '18 at 10:12
  • 1
    @DávidPásztor Sorry I must have misunderstood. Still, this should be easily researchable. – ABeard89 Jun 19 '18 at 10:14
  • @Harsha you should consider adding an `updated` variable to your `Example` objects in order to know which items should be merged – thxou Jun 19 '18 at 10:20
  • @Harsha did you find answer or need more explanation? – えるまる Jun 19 '18 at 10:57
  • @DávidPásztor, check the edited. Merged should contain both first and second – Harsha Jun 19 '18 at 12:34
  • @Harsha did you find you answer? – えるまる Sep 07 '19 at 07:10

5 Answers5

6
import Foundation

struct Example: Codable {
    var name: String
    var dob: String
    var address: String
}

var first: [Example] = []
var second: [Example] = []

first.append(Example(name: "Arun", dob: "01-01-1994", address: "Tirupati"))
first.append(Example(name: "Balaji", dob: "01-01-1994", address: "Tirupati"))
first.append(Example(name: "Prasanth", dob: "01-01-1994", address: "Tirupati"))

second.append(Example(name: "Arun", dob: "01-01-1994", address: "Kadapa"))
second.append(Example(name: "Balaji", dob: "01-01-1994", address: "Tirupati"))
second.append(Example(name: "Prasanth", dob: "01-01-1994", address: "Tirupati"))
second.append(Example(name: "Harsha", dob: "01-01-1994", address: "Tirupati"))

first = second + first.filter { element in
    return !second.contains { $0.name == element.name }
}

[{name "Arun", dob "01-01-1994", address "Kadapa"},
{name "Balaji", dob "01-01-1994", address "Tirupati"},
{name "Prasanth", dob "01-01-1994", address "Tirupati"},
{name "Harsha", dob "01-01-1994", address "Tirupati"}]

えるまる
  • 2,409
  • 3
  • 24
  • 44
  • Have you actually tried this with the `Example` type in the question? This doesn't do what OP wants to do at all. – Dávid Pásztor Jun 19 '18 at 10:02
  • 1
    Please don't post an answer if you're unsure about what your code does. Your code still doesn't provide the expected output. It simply takes the union of the two arrays, not handling duplicate names at all... – Dávid Pásztor Jun 19 '18 at 10:14
4

If the order of elements in the merged array does not matter then you can use a dictionary which maps each name to the (most recent) element with that name (similar to what user2760845 suggested).

var dict: [String: Example] = [:]
for elem in first { dict[elem.name] = elem }
for elem in second { dict[elem.name] = elem }
let merged = Array(dict.values)

Iterating over the second array overwrites entries for the same name from the first array.

Or, as an (obfuscated?) one-liner:

let merged = Array(Dictionary([first, second].joined().map { ($0.name, $0)}, uniquingKeysWith: { $1 }).values)
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • one-liner is probably the easiest and efficient way. – Parth Adroja Jun 19 '18 at 11:19
  • @ParthAdroja: That really depends on the number of elements, perhaps their size, and how many "duplicates" exist. As in https://stackoverflow.com/a/50926135/1187415, it avoids a linear search, which *might* be advantageous for large arrays. For small/medium-sized arrays there may be no significant difference between all the solutions given in the various answers. – Martin R Jun 19 '18 at 11:42
  • Totally agree with you. – Parth Adroja Jun 19 '18 at 12:16
1

You can merge the two Example arrays by updating duplicate names with the information from the second Array using below function. This function also works if second doesn't contain all elements of first.

func merge(first: [Example], second: [Example]) -> [Example] {
    var secondCopy = second
    let updatedFirst = first.map({ person -> Example in
        let updatedIndex = secondCopy.index(where: {$0.name == person.name})
        if let updatedIndex = updatedIndex {
            let updated = secondCopy[updatedIndex]
            secondCopy.remove(at: updatedIndex)
            return updated
        } else {
            return person
        }
    })
    return updatedFirst + secondCopy
}

let merged = merge(first: first, second: second)

[{name "Arun", dob "01-01-1994", address "Kadapa"},

{name "Balaji", dob "01-01-1994", address "Tirupati"},

{name "Prasanth", dob "01-01-1994", address "Tirupati"},

{name "Harsha", dob "01-01-1994", address "Tirupati"}]

Tiago Mendes
  • 4,572
  • 1
  • 29
  • 35
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
1

Below is a simple yet quite speed-efficient implementation. If you need to keep the relative order of elements the solution needs a little tweak.

var secondDict = [String: Example]()
var merge = [Example]()
for eg2 in second {
    secondDict[eg2.name] = eg2
}
for eg1 in first {
    if let eg2 = secondDict.removeValue(forKey: eg1.name) {
        merge.append(eg2)
    } else {
        merge.append(eg1)
    }
}
for eg2 in secondDict.values {
    merge.append(eg2)
}
// 'merge' is the merged array
user2760845
  • 124
  • 5
0

You can try this, not sure if this is what you need.

struct Example: Codable, Hashable {

    var hashValue: Int {
        return self.name.hashValue
    }

    var name: String
    var dob: String
    var address: String
}

func ==(lhs: Example, rhs: Example) -> Bool {
    return lhs.name == rhs.name
}

func merge(first: [Example], second: [Example]) -> [Example] {
    return Array(Set(second).union(first))
}

let merged = merge(first: first, second: second) // is this what you need?
koropok
  • 1,393
  • 13
  • 19
  • Ask OP in questions if unsure about the answer. The question clearly states that OP doesn't simply need a union, but want to update duplicate values with the properties from `second`. – Dávid Pásztor Jun 19 '18 at 10:17
  • hmm i thought OP wants to have all 4 objects in the final array, and not just updating the first 3 objects in the first array. my bad. – koropok Jun 19 '18 at 10:20