0

I have two arrays (both County and City are conformed to Comparable):

let array1 = [
  Country(code: "US", cities: [
    City(name: "Dallas"),
    City(name: "New York")
  ]),
  Country(code: "UK", cities: [
    City(name: "London"),
    City(name: "Manchester")
  ])
]

let array2 = [
  Country(code: "DE", cities: [
    City(name: "Munich"),
    City(name: "Leipzig")
  ]),
  Country(code: "US", cities: [
    City(name: "Seattle")
    City(name: "New York")
  ]),
  Country(code: "UK", cities: [
    City(name: "London"),
    City(name: "Birmingham"),
    City(name: "Manchester")
  ])
]

So far I managed to combine those only comparing country codes:

let mergedArray = array1 + array2.filter { country in
  return !array1.contains { $0.code == country.code }
}

How do I account for those repeated cities inside? Basically to get this:

let mergedArray = [
  Country(code: "DE", cities: [
    City(name: "Munich"),
    City(name: "Leipzig")
  ]),
  Country(code: "US", cities: [
    City(name: "Dallas"),
    City(name: "Seattle")
    City(name: "New York")
  ]),
  Country(code: "UK", cities: [
    City(name: "London"),
    City(name: "Birmingham"),
    City(name: "Manchester")
  ])
]

Tried also (isn't adding those extra cities for UK):

func combine<T>(_ arrays: Array<T>?...) -> Set<T> {
  return arrays.compactMap{$0}.compactMap{Set($0)}.reduce(Set<T>()){$0.union($1)}
}

and tried this but no luck at all (a lot of duplicates):

let mergedArray = array1 + array2.filter { country in
  return !array1.contains { $0.code == country.code }  ||
    (!array1.contains(where: { $0.cities?.contains(where: country.cities!.contains) as! Bool }))
}
3
  • I would load the first array into a dictionary, keyed by country name. The iterate over your second array. Check if the dictionary has an entry for the country, if not, just add the country to the dictionary. If it does you need to merge the cities. Finally convert the dictionary back to an array. To merge the city arrays you can convert each to a set and the convert the union of the two sets back to an array. Commented Apr 6, 2021 at 21:59
  • Do you wanna attempt with as little lines as possible or something? If not, can't you just iterate over array1 using a ForEach and for all countries matching the code inside array2 just add the city objects into the Country object in array1. Append all the "non-matching" country codes at the end Commented Apr 6, 2021 at 22:06
  • Yeah, good ideas @Paulw11 and @Rikh but I just wanted the cleanest solution if possible. Of course, I can manually go through the arrays and do it with for and if but that wouldn't be fun :) Commented Apr 7, 2021 at 7:51

1 Answer 1

2

Can you make City conform to Hashable? If so, try this:

var dict = [String: Set<City>]() // code -> cities

for country in array1 + array2 {
    dict[country.code, default: Set<City>()].formUnion(country.cities)
}

// convert back to array
let mergedArray = dict.map { code, cities in
    Country(code: code, cities: Array(cities))
}
Sign up to request clarification or add additional context in comments.

1 Comment

Ah! That's genius :) so basically items in Set can't have duplicates so that works perfectly. And 4 lines of code basically! Thanks!

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.