Tuples are Equatable
(given that their elements are Equatable
) up to arity 6, which we could make use of here to zip the three arrays into a sequence of 3-tuples, identifying repeated 3-tuple elements, and removing the indices associated with these tuples from the original three arrays. Tuples are not, however, Hashable
, so instead of using 3-tuples we could fall back on a utility Hashable
type storing the three values (that the 3-tuple did type anonymously).
Utility type:
struct ZippedElement: Hashable {
let a: String
let b: Int
let c: String
init(_ a: String, _ b: Int, _ c: String) {
self.a = a
self.b = b
self.c = c
}
// Use a very simple common hashValue calculation, simply
// falling back on the hashValue of the Int member.
var hashValue: Int { return b.hashValue }
static func ==(lhs: ZippedElement, rhs: ZippedElement) -> Bool {
return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c
}
}
Which allows us to perform the filtering/mutating operations on array1
through array3
as follows:
var seen = Set<ZippedElement>()
zip(zip(array1, array2), array3)
.map { ZippedElement($0.0, $0.1, $1) }
.enumerated().filter { !seen.insert($1).inserted }
.map { $0.offset }.reversed()
.forEach {
array1.remove(at: $0)
array2.remove(at: $0)
array3.remove(at: $0)
}
With, as a result, the last three elements being removed in each array:
print(array1) // ["a", "b", "c", "d"]
print(array2) // [1, 2, 3, 4]
print(array3) // ["aa", "bb", "cc", "dd"]
Your example data setup doesn't pose many challenges for the different solutions here, however, so @dasblinkenlight asks a good question:
Would it change the desired result if I replaced the last "dd"
of array3
with "dx"
?
In this case, I believe most of us assume that the 7th element in all the original arrays should be kept, as the "vertical" zip combination over all three arrays, for the 7th element (/column), is unique.
Applying the same approach as above for such a modified example:
var array1 = ["a", "b", "c", "d", "d", "c", "d"]
var array2 = [ 1, 2, 3, 4, 4, 3, 4]
var array3 = ["aa", "bb", "cc", "dd", "dd", "cc", "dx"]
/* ^^ obs */
var seen = Set<ZippedElement>()
zip(zip(array1, array2), array3)
.map { ZippedElement($0.0, $0.1, $1) }
.enumerated().filter { !seen.insert($1).inserted }
.map { $0.offset }.reversed()
.forEach {
print($0)
array1.remove(at: $0)
array2.remove(at: $0)
array3.remove(at: $0)
}
print(array1) // ["a", "b", "c", "d", "d"]
print(array2) // [1, 2, 3, 4, 4]
print(array3) // ["aa", "bb", "cc", "dx"]
/* ^^ ok */
Another comment to your question is asked by @SteveKuo, stating what is on probably on most of our minds (in excess of a somewhat fun algorithmic exercise) for all questions such as this one (index-tracking separate arrays ...):
Seems like a more appropriate data structure is to create struct/class/tuple of the array1/2/3 attributes.
And I believe this is the core answer you should take with you here, so even if you explicitly state
... ps. 3 arrays have to be in separated arrays
You probably want a single array of a custom type instead.