Swift difference(from:) method (with examples)

Updated: May 4, 2023 By: Khue Post a comment

Overview

Introduced in Swift 5, difference(from:) is a built-in method that allows you to compare two arrays and return the differences between them. This method is particularly useful when you need to identify what elements have been added or removed between two versions of an array. It achieves this by providing a collection of CollectionDifference elements, where each element represents a single insertion or removal operation.

The difference(from:) method is part of the BidirectionalCollection protocol, which means it can be used with any collection type that conforms to this protocol, such as arrays, strings, and other bidirectional collections.

Syntax

The syntax for the difference(from:) method is as follows:

func difference<C>(from other: C) -> CollectionDifference<Element> where C : BidirectionalCollection, Self.Element == C.Element, Self.Element : Hashable

This method has a single parameter, other, which is the collection you want to compare with the calling collection. Both collections must conform to the BidirectionalCollection protocol, and their elements must be of the same type and conform to the Hashable protocol.

The difference(from:) method returns a CollectionDifference<Element> value, which is a collection of CollectionDifference.Change elements, where each element represents either an insertion or a removal operation. It conforms to the Collection protocol, which means you can iterate over it, access its elements by index, and use its methods like count, first, last, etc.

Process the result

You can iterate over the CollectionDifference<Element> object returned by the difference(from:) method using a for loop or forEach closure, and process each change according to your needs like so:

differences.forEach { change in
    switch change {
      case let .insert(offset, element, _):
          print("Insert \(element) at index \(offset)")
      case let .remove(offset, element, _):
          print("Remove \(element) at index \(offset)")
    }
}

For more clarity, please see examples 2, 3, and 4 in the following section.

Examples

Now that we have an understanding of the difference(from:) method, let’s dive into some examples to see how it can be used in practice.

Example 1: Working with arrays of numbers

In this example, we will compare two arrays containing integers:

let array1 = [1, 2, 3, 4, 5]
let array2 = [1, 3, 4, 6, 7]

let difference = array1.difference(from: array2)
dump(difference)

Output:

▿ Swift.CollectionDifference<Swift.Int>
  ▿ insertions: 2 elements
    ▿ Swift.CollectionDifference<Swift.Int>.Change.insert
      ▿ insert: (3 elements)
        - offset: 1
        - element: 2
        - associatedWith: nil
    ▿ Swift.CollectionDifference<Swift.Int>.Change.insert
      ▿ insert: (3 elements)
        - offset: 4
        - element: 5
        - associatedWith: nil
  ▿ removals: 2 elements
    ▿ Swift.CollectionDifference<Swift.Int>.Change.remove
      ▿ remove: (3 elements)
        - offset: 3
        - element: 6
        - associatedWith: nil
    ▿ Swift.CollectionDifference<Swift.Int>.Change.remove
      ▿ remove: (3 elements)
        - offset: 4
        - element: 7
        - associatedWith: nil

The output seems too verbose. Don’t worry, we will do something with it in the next examples.

Example 2: Working with arrays of strings

In this example, we will use the difference(from:) method with two arrays of strings and use the forEach() method to refine the result.

let oldList = ["apple", "banana", "cherry"]
let newList = ["apple", "blueberry", "cherry"]

let differences = oldList.difference(from: newList)

if differences.count > 0 {
    print(
        "You have make \(differences.count) changes in newList to make it become equal to oldList:")
    differences.forEach { change in
        switch change {
        case let .insert(offset, element, _):
            print("Insert \(element) at index \(offset)")
        case let .remove(offset, element, _):
            print("Remove \(element) at index \(offset)")
        }
    }
}

Output:

You have make 2 changes in newList to make it become equal to oldList:
Remove blueberry at index 1
Insert banana at index 1

Example 3: Applying changes to an array

In this example, we have two arrays of words: words1 and words2. What we will do is to use the difference(from:) method to find the difference between them, then make necessary changes to words1 to make it equal to words2.

var words1 = ["Sling", "Academy", "Swift", "iOS"]
let words2 = ["Sling", "Academy", "Hello", "World"]

let differences = words2.difference(from: words1)

for change in differences {
    switch change {
    case let .insert(offset, element, _):
        words1.insert(element, at: offset)
    case let .remove(offset, _, _):
        words1.remove(at: offset)
    }
}

print(words1)

Output:

["Sling", "Academy", "Hello", "World"]

Example 4: Working with arrays of custom types

In this example, we will find the difference between two arrays containing custom Person structs. To use the difference(from:) method with custom types, they must conform to the Hashable protocol.

struct Person: Equatable, Hashable {
    let id: Int
    let name: String
}

let oldPeople = [
    Person(id: 1, name: "Mr. A"),
    Person(id: 2, name: "Mr. B"),
    Person(id: 3, name: "Mr. C"),
]

let newPeople = [
    Person(id: 1, name: "Mr. A"),
    Person(id: 4, name: "Mr. Dee"),
    Person(id: 3, name: "Mr. C"),
]

let differences = oldPeople.difference(from: newPeople)

differences.forEach { change in
    switch change {
    case let .insert(offset, person, _):
        print("Insert \(person.name) at index \(offset)")
    case let .remove(offset, person, _):
        print("Remove \(person.name) at index \(offset)")
    }
}

Output:

Remove Mr. Dee at index 1
Insert Mr. B at index 1

Conclusion

Remember to always consider the performance implications of using the difference(from:) method, as it can be computationally expensive for large collections. When used wisely, however, this method can greatly simplify your code and help you build more efficient and robust applications in Swift.

Happy coding & have a nice day!