1

I have two arrays

let a1 = [obj1, obj2, obj3]
let a2 = [obj3, obj2, obj1]

Assume that the array elements are custom objects which are not sortable. I just want to check if the Arrays contain the same elements, in any order.

I tried this:

if a1==a2 { print("S1 is the same as S2") }
else { print("S1 is not the same as S2") }

but I get "S1 is not the same as S2" as output.

All I could think of are two solutions

  • Sort and compare (doesn't work if elements are not sortable, say complex numbers)
  • Subtract one array from the other

Is there any built-in function or operation to check if they are equal, without considering order?

I found How do I check in Swift if two arrays contain the same elements regardless of the order in which those elements appear in? which has the answer only for sortable Arrays.

Community
  • 1
  • 1
Saravanabalagi Ramachandran
  • 8,551
  • 11
  • 53
  • 102

3 Answers3

8

If the items are unique and equatable (as in your example), convert to a Set and compare the Sets:

let a1 = [1, 2, 3]
let a2 = [3, 2, 1]
Set(a1) == Set(a2)

With some futzing, this can be made to work for an arbitrary class:

class Person : NSObject {
    let name: String
    init(name:String) {self.name = name}
    override func isEqual(other: AnyObject?) -> Bool {
        return other is Person && (other as! Person).name == self.name
    }
    override var hashValue : Int {
        return self.name.hashValue
    }
}

let a1 = [Person(name:"Matt"), Person(name:"Sam")]
let a2 = [Person(name:"Sam"), Person(name:"Matt")]
Set(a1) == Set(a2)
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Perfect, I was expecting something like that :) Thumbs up Works great for array of complex numbers – Saravanabalagi Ramachandran Jun 18 '16 at 18:47
  • Brilliant answer but @matt in case I add an extra element in one of the array it still says true. Any workaround for that? – Mtoklitz113 Jun 18 '16 at 18:48
  • 1
    @Dershowitz123 I said in my answer _if the items are unique_. Obviously a Set conversion will unique the array and so the extra item is ignored. You can use NSCountedSet to solve that problem. – matt Jun 18 '16 at 18:50
  • Actually if you add an extra element in one of the arrays (only in one of them) then the two arrays will have a different number of elements, so quickly comparing their count properties will show the arrays are different. However, you could have array1 with elements 1,2,3,3 and array2 with elements 1,2,2,3. In this case their count properties would return both 4 elements and converting to sets and comparing them would return they are equal (which wouldn't be correct). So, what @matt says, use NSCountedSet instead. – Albert Mata Jun 19 '16 at 14:06
3

You can convert them into Set or NSSet instances which are unsorted by definition and compare those. Better yet, instead of using arrays at all, consider using sets in the first place.

let a1 = [1, 4, 5]
let a2 = [4, 5, 1]
let s1 = NSSet(array: a1)
let s2 = NSSet(array: a2)
print(s1 == s2) // Prints "true"

If objects may appear multiple times in your arrays, you need to use a NSCountedSet instead which also counts how often each object occurs in a set:

let a1 = [1, 1, 2]
let a2 = [1, 2, 2]

let s1 = NSSet(array: a1)
let s2 = NSSet(array: a2)
print(s1 == s2) // Prints "true"

let c1 = NSCountedSet(array: a1)
let c2 = NSCountedSet(array: a2)
print(c1 == c2) // Prints "false"
DarkDust
  • 90,870
  • 19
  • 190
  • 224
2

These 2 solutions also work if the arrays have duplicates.

Scenario 1: elements are sortable

let a1 = [1, 2, 3]
let a2 = [3, 2, 1]

let equals = a1.sorted() == a2.sorted()

Time Complexity: We need to sort both arrays so time complexity is O(n * log n) + O( m * log m)

Space complexity: A temporary copy of both arrays is created so the required space is O(n) + O (m).

Where n is the number of elements in a1 and m is the number of elements in a2.

Scenario 2: elements are NOT sortable

func equals<Element:Equatable>(listA:[Element], listB:[Element]) -> Bool {
    guard listA.count == listB.count else { return false }

    var listB = listB

    for a in listA {
        if let indexB = listB.indexOf(a) {
            listB.removeAtIndex(indexB)
        } else {
            return false
        }
    }

    return listB.isEmpty
}

Time Complexity: We are iterating the first array and each time we perform a linear search into the second array so O(n * m)

Space Complexity: We created a copy of the second array so O(m)

Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148