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.

13
  • Possible duplicate of How do I concatenate or merge arrays in Swift? Commented Jun 19, 2018 at 9:54
  • 3
    Yes, but concatenating is different from merging. Concatenating may have duplicates. Commented Jun 19, 2018 at 9:59
  • Is the order important? Else maybe a (NS)Set with Equatable or something like that, or NSOrderedSet? stackoverflow.com/a/44228470/1801544 (after Equatable?) Commented Jun 19, 2018 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. Commented Jun 19, 2018 at 10:07
  • 1
    @DávidPásztor Sorry I must have misunderstood. Still, this should be easily researchable. Commented Jun 19, 2018 at 10:14

5 Answers 5

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"}]

Sign up to request clarification or add additional context in comments.

2 Comments

Have you actually tried this with the Example type in the question? This doesn't do what OP wants to do at all.
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...
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)

3 Comments

one-liner is probably the easiest and efficient way.
@ParthAdroja: That really depends on the number of elements, perhaps their size, and how many "duplicates" exist. As in 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.
Totally agree with you.
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

Comments

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"}]

Comments

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?

2 Comments

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.