Swift: Binary search for standard array? - arrays

I have a sorted array and want to do binary search on it.
So I'm asking if something is already available in Swift library like sort etc.? Or is there a type independend version available?
Of course I could write it by my own, but I like to avoid reinventing the wheel again.

Here's my favorite implementation of binary search. It's useful not only for finding the element but also for finding the insertion index. Details about assumed sorting order (ascending or descending) and behavior with respect to equal elements are controlled by providing a corresponding predicate (e.g. { $0 < x } vs { $0 > x } vs { $0 <= x } vs { $0 >= x }). The comment unambiguously says what exactly does it do.
extension RandomAccessCollection {
/// Finds such index N that predicate is true for all elements up to
/// but not including the index N, and is false for all elements
/// starting with index N.
/// Behavior is undefined if there is no such N.
func binarySearch(predicate: (Element) -> Bool) -> Index {
var low = startIndex
var high = endIndex
while low != high {
let mid = index(low, offsetBy: distance(from: low, to: high)/2)
if predicate(self[mid]) {
low = index(after: mid)
} else {
high = mid
}
}
return low
}
}
Example usage:
(0 ..< 778).binarySearch { $0 < 145 } // 145

Here's a generic way to use binary search:
func binarySearch<T:Comparable>(_ inputArr:Array<T>, _ searchItem: T) -> Int? {
var lowerIndex = 0
var upperIndex = inputArr.count - 1
while (true) {
let currentIndex = (lowerIndex + upperIndex)/2
if(inputArr[currentIndex] == searchItem) {
return currentIndex
} else if (lowerIndex > upperIndex) {
return nil
} else {
if (inputArr[currentIndex] > searchItem) {
upperIndex = currentIndex - 1
} else {
lowerIndex = currentIndex + 1
}
}
}
}
var myArray = [1,2,3,4,5,6,7,9,10]
if let searchIndex = binarySearch(myArray, 5) {
print("Element found on index: \(searchIndex)")
}

I use an extension on RandomAccessCollection implementing bisectToFirstIndex(where:) and taking a predicate.
It takes a test predicate, and returns the index of the first element to pass the test.
If there is no such index, it returns nil.
If the Collection is empty, it returns nil.
Example
let a = [1,2,3,4]
a.map{$0>=3}
// returns [false, false, true, true]
a.bisectToFirstIndex {$0>=3}
// returns 2
Important
You need to ensure test never returns a false for any index after an index it has said true for. This is equivalent to the usual precondition that binary search requires your data to be in order.
Specifically, you must not do a.bisectToFirstIndex {$0==3}. This will not work correctly.
Why?
bisectToFirstIndex is useful because it lets you find ranges of stuff in your data. By adjusting the test, you can find the lower and upper limits of "stuff".
Here's some data:
let a = [1,1,1, 2,2,2,2, 3, 4, 5]
We can find the Range of all the 2s like this…
let firstOf2s = a.bisectToFirstIndex { $ 0>= 2 }
let endOf2s = a.bisectToFirstIndex { $0 > 2 }
let rangeOf2s = firstOf2s ..< endOf2s
Example Application
I use this in an implementation of layoutAttributesForElementsInRect. My UICollectionViewCells are stored sorted vertically in an array. It's easy to write a pair of calls that will find all cells that are within a particular rectangle and exclude any others.
Code
extension RandomAccessCollection {
public func bisectToFirstIndex(where predicate: (Element) throws -> Bool) rethrows -> Index? {
var intervalStart = startIndex
var intervalEnd = endIndex
while intervalStart != intervalEnd {
let intervalLength = distance(from: intervalStart, to: intervalEnd)
guard intervalLength > 1 else {
return try predicate(self[intervalStart]) ? intervalStart : nil
}
let testIndex = index(intervalStart, offsetBy: (intervalLength - 1) / 2)
if try predicate(self[testIndex]) {
intervalEnd = index(after: testIndex)
}
else {
intervalStart = index(after: testIndex)
}
}
return nil
}
}
Updates…
The implementation here extends RandomAccessCollection and I've updated the code to build with the current Swift version (5 or something).
A Binary Search Caution
Binary searches are notoriously hard to correctly code. You really should read that link to find out just how common mistakes in their implementation are, but here is an extract:
When Jon Bentley assigned it as a problem in a course for professional programmers, he found that an astounding ninety percent failed to code a binary search correctly after several hours of working on it, and another study shows that accurate code for it is only found in five out of twenty textbooks. Furthermore, Bentley's own implementation of binary search, published in his 1986 book Programming Pearls, contains an error that remained undetected for over twenty years.
Given that last point, here is a test for this code. It passes! The testing isn't exhaustive – so there may certainly still be errors.
Tests
final class Collection_BisectTests: XCTestCase {
func test_bisect() {
for length in 0...100 {
let collection = 0 ... length
let targets = -4 ... length + 4
for toFind in targets {
let bisectIndex = collection.bisectToFirstIndex { $0 > toFind }
let expectIndex = collection.firstIndex { $0 > toFind }
XCTAssertEqual(bisectIndex, expectIndex, "Finding \(toFind+1) in 0...\(length)")
}
}
}
}

extension ArraySlice where Element: Comparable {
func binarySearch(_ value: Element) -> Int? {
guard !isEmpty else { return nil }
let midIndex = (startIndex + endIndex) / 2
if value == self[midIndex] {
return midIndex
} else if value > self[midIndex] {
return self[(midIndex + 1)...].binarySearch(value)
} else {
return self[..<midIndex].binarySearch(value)
}
}
}
extension Array where Element: Comparable {
func binarySearch(_ value: Element) -> Int? {
return self[0...].binarySearch(value)
}
}
This is, in my opinion, very readable and leverages the fact that Swift's ArraySlice is a view on Array and retains the same indexes as the original Array with which it shares the storage so, in absence of mutations (like in this case), it is therefore very efficient.

here is binary search using while syntax
func binarySearch<T: Comparable>(_ a: [T], key: T) -> Int? {
var lowerBound = 0
var upperBound = a.count
while lowerBound < upperBound {
let midIndex = lowerBound + (upperBound - lowerBound) / 2
if a[midIndex] == key {
return midIndex
} else if a[midIndex] < key {
lowerBound = midIndex + 1
} else {
upperBound = midIndex
}
}
return nil
}

Here is an implementation for a sorted array of strings.
var arr = ["a", "abc", "aabc", "aabbc", "aaabbbcc", "bacc", "bbcc", "bbbccc", "cb", "cbb", "cbbc", "d" , "defff", "deffz"]
func binarySearch(_ array: [String], value: String) -> String {
var firstIndex = 0
var lastIndex = array.count - 1
var wordToFind = "Not founded"
var count = 0
while firstIndex <= lastIndex {
count += 1
let middleIndex = (firstIndex + lastIndex) / 2
let middleValue = array[middleIndex]
if middleValue == value {
wordToFind = middleValue
return wordToFind
}
if value.localizedCompare(middleValue) == ComparisonResult.orderedDescending {
firstIndex = middleIndex + 1
}
if value.localizedCompare(middleValue) == ComparisonResult.orderedAscending {
print(middleValue)
lastIndex = middleIndex - 1
}
}
return wordToFind
}
//print d
print(binarySearch(arr, value: "d"))

Another implementation: if you want to have your structs or classes searchable without making them Comparable, make them BinarySearchable instead:
public protocol BinarySearchable {
associatedtype C: Comparable
var searchable: C { get }
}
public extension Array where Element: BinarySearchable {
func binarySearch(_ prefix: Element.C) -> Index {
var low = 0
var high = count
while low != high {
let mid = (low + high) / 2
if self[mid].searchable < prefix {
low = mid + 1
} else {
high = mid
}
}
return low
}
}
Example usage for a struct that should be sorted and searched by name:
struct Country: BinraySearchable {
var code: String
var name: String
var searchable: String { name }
}
// Suppose you have a list of countries sorted by `name`, you want to find
// the index of the first country whose name starts with "United", others
// will follow:
let index = listOfCountries.binarySearch("United")

And for completeness, here's a entirely pattern matching based implementation:
extension Collection where Element: Comparable {
func binarySearch(for element: Element) -> Index? {
switch index(startIndex, offsetBy: distance(from: startIndex, to: endIndex) / 2) {
case let i where i >= endIndex: return nil
case let i where self[i] == element: return i
case let i where self[i] > element: return self[..<i].binarySearch(for: element)
case let i: return self[index(after: i)..<endIndex].binarySearch(for: element)
}
}
}
The above code should work with any kind of collections, sliced or not sliced, zero offset-ed or non-zero offset-ed.

Here is how you create a binary search function in swift 5, in this example I assume that the item you are looking for is guaranteed to be in the list, however if your item is not guaranteed to be in the list then you can run this code to check first:
yourList.contains(yourItem) //will return true or false
Here is the binary search function:
override func viewDidLoad() {
super.viewDidLoad()
print(binarySearch(list: [1, 2, 4, 5, 6], num: 6)) //returns 4
}
func binarySearch(list: [Int], num: Int) -> Int //returns index of num
{
var firstIndex = 0
var lastIndex = list.count - 1
var middleIndex = (firstIndex + lastIndex) / 2
var middleValue = list[middleIndex]
while true //loop until we find the item we are looking for
{
middleIndex = (firstIndex + lastIndex) / 2 //getting the list's middle index
middleValue = list[middleIndex]
if middleValue > num
{
lastIndex = middleIndex - 1 //get the left side of the remaining list
}
else if middleValue < num
{
firstIndex = middleIndex + 1 //get the right side of the remaining list
}
else if middleValue == num
{
break //found the correct value so we can break out of the loop
}
}
return middleIndex
}
I have made a youtube video explaining this here

Here's a better implementation that returns more than one index, if there are more than 1 in the array.
extension Array where Element: Comparable {
/* Array Must be sorted */
func binarySearch(key: Element) -> [Index]? {
return self.binarySearch(key, initialIndex: 0)
}
private func binarySearch(key: Element, initialIndex: Index) -> [Index]? {
guard count > 0 else { return nil }
let midIndex = count / 2
let midElement = self[midIndex]
if key == midElement {
// Found!
let foundIndex = initialIndex + midIndex
var indexes = [foundIndex]
// Check neighbors for same values
// Check Left Side
var leftIndex = midIndex - 1
while leftIndex >= 0 {
//While there is still more items on the left to check
print(leftIndex)
if self[leftIndex] == key {
//If the items on the left is still matching key
indexes.append(leftIndex + initialIndex)
leftIndex--
} else {
// The item on the left is not identical to key
break
}
}
// Check Right side
var rightIndex = midIndex + 1
while rightIndex < count {
//While there is still more items on the left to check
if self[rightIndex] == key {
//If the items on the left is still matching key
indexes.append(rightIndex + initialIndex)
rightIndex++
} else {
// The item on the left is not identical to key
break
}
}
return indexes.sort{ return $0 < $1 }
}
if count == 1 {
guard let first = first else { return nil }
if first == key {
return [initialIndex]
}
return nil
}
if key < midElement {
return Array(self[0..<midIndex]).binarySearch(key, initialIndex: initialIndex + 0)
}
if key > midElement {
return Array(self[midIndex..<count]).binarySearch(key, initialIndex: initialIndex + midIndex)
}
return nil
}
}

By recursive binary search,
func binarySearch(data : [Int],search: Int,high : Int,low:Int) -> Int? {
if (low > high)
{
return nil
}
let mid = low + (low + high)/2
if (data[mid] == search) {
return mid
}
else if (search < data[mid]){
return binarySearch(data: data, search: search, high: high-1, low: low)
}else {
return binarySearch(data: data, search: search, high: high, low: low+1)
}
}
Input : let arry = Array(0...5) // [0,1,2,3,4,5]
print(binarySearch(data: arry, search: 0, high: arry.count-1, low: 0))

Here's a full example with several test cases for Swift 3.1. There is no chance that this is faster than the default implementation, but that's not the point. Array extension is at the bottom:
// BinarySearchTests.swift
// Created by Dan Rosenstark on 3/27/17
import XCTest
#testable import SwiftAlgos
class BinarySearchTests: XCTestCase {
let sortedArray : [Int] = [-25, 1, 2, 4, 6, 8, 10, 14, 15, 1000]
func test5() {
let traditional = sortedArray.index(of: 5)
let newImplementation = sortedArray.indexUsingBinarySearch(of: 5)
XCTAssertEqual(traditional, newImplementation)
}
func testMembers() {
for item in sortedArray {
let traditional = sortedArray.index(of: item)
let newImplementation = sortedArray.indexUsingBinarySearch(of: item)
XCTAssertEqual(traditional, newImplementation)
}
}
func testMembersAndNonMembers() {
for item in (-100...100) {
let traditional = sortedArray.index(of: item)
let newImplementation = sortedArray.indexUsingBinarySearch(of: item)
XCTAssertEqual(traditional, newImplementation)
}
}
func testSingleMember() {
let sortedArray = [50]
for item in (0...100) {
let traditional = sortedArray.index(of: item)
let newImplementation = sortedArray.indexUsingBinarySearch(of: item)
XCTAssertEqual(traditional, newImplementation)
}
}
func testEmptyArray() {
let sortedArray : [Int] = []
for item in (0...100) {
let traditional = sortedArray.index(of: item)
let newImplementation = sortedArray.indexUsingBinarySearch(of: item)
XCTAssertEqual(traditional, newImplementation)
}
}
}
extension Array where Element : Comparable {
// self must be a sorted Array
func indexUsingBinarySearch(of element: Element) -> Int? {
guard self.count > 0 else { return nil }
return binarySearch(for: element, minIndex: 0, maxIndex: self.count - 1)
}
private func binarySearch(for element: Element, minIndex: Int, maxIndex: Int) -> Int? {
let count = maxIndex - minIndex + 1
// if there are one or two elements, there is no futher recursion:
// stop and check one or both values (and return nil if neither)
if count == 1 {
return element == self[minIndex] ? minIndex : nil
} else if count == 2 {
switch element {
case self[minIndex]: return minIndex
case self[maxIndex]: return maxIndex
default: return nil
}
}
let breakPointIndex = Int(round(Double(maxIndex - minIndex) / 2.0)) + minIndex
let breakPoint = self[breakPointIndex]
let splitUp = (breakPoint < element)
let newMaxIndex : Int = splitUp ? maxIndex : breakPointIndex
let newMinIndex : Int = splitUp ? breakPointIndex : minIndex
return binarySearch(for: element, minIndex: newMinIndex, maxIndex: newMaxIndex)
}
}
This is quite homemade, so... caveat emptor. It does work and does do binary search.

Simple solution in Swift 5:
func binarySerach(list: [Int], item: Int) -> Int? {
var low = 0
var high = list.count - 1
while low <= high {
let mid = (low + high) / 2
let guess = list[mid]
if guess == item {
return mid
} else if guess > item {
high = mid - 1
} else {
low = mid + 1
}
}
return nil
}
let myList = [1,3,4,7,9]
print(binarySerach(list: myList, item: 9))
//Optional(4)

Details
Swift 5.2, Xcode 11.4 (11E146)
Solution
import Foundation
extension RandomAccessCollection where Element: Comparable {
private func binarySearchIteration(forIndexOf value: Element, in range: Range<Index>? = nil,
valueDetected: ((Index, _ in: Range<Index>) -> Index?)) -> Index? {
let range = range ?? startIndex..<endIndex
guard range.lowerBound < range.upperBound else { return nil }
let size = distance(from: range.lowerBound, to: range.upperBound)
let middle = index(range.lowerBound, offsetBy: size / 2)
switch self[middle] {
case value: return valueDetected(middle, range) ?? middle
case ..<value: return binarySearch(forIndexOf: value, in: index(after: middle)..<range.upperBound)
default: return binarySearch(forIndexOf: value, in: range.lowerBound..<middle)
}
}
func binarySearch(forIndexOf value: Element, in range: Range<Index>? = nil) -> Index? {
binarySearchIteration(forIndexOf: value, in: range) { currentIndex, _ in currentIndex }
}
func binarySearch(forFirstIndexOf value: Element, in range: Range<Index>? = nil) -> Index? {
binarySearchIteration(forIndexOf: value, in: range) { currentIndex, range in
binarySearch(forFirstIndexOf: value, in: range.lowerBound..<currentIndex)
}
}
func binarySearch(forLastIndexOf value: Element, in range: Range<Index>? = nil) -> Index? {
binarySearchIteration(forIndexOf: value, in: range) { currentIndex, range in
binarySearch(forFirstIndexOf: value, in: index(after: currentIndex)..<range.upperBound)
}
}
func binarySearch(forIndicesRangeOf value: Element, in range: Range<Index>? = nil) -> Range<Index>? {
let range = range ?? startIndex..<endIndex
guard range.lowerBound < range.upperBound else { return nil }
guard let currentIndex = binarySearchIteration(forIndexOf: value, in: range, valueDetected: { index, _ in index
}) else { return nil }
let firstIndex = binarySearch(forFirstIndexOf: value, in: range.lowerBound ..< index(after: currentIndex)) ?? currentIndex
let lastIndex = binarySearch(forFirstIndexOf: value, in: index(after: currentIndex) ..< range.upperBound) ?? currentIndex
return firstIndex..<index(after: lastIndex)
}
}
Usage
//let array = ["one", "two", "three", "three", "three", "three", "three", "four", "five", "five"]
//let value = "three"
let array = [1, 2, 3, 3, 3, 3, 3, 4, 5, 5]
let value = 3
print(array.binarySearch(forFirstIndexOf: value))
print(array.binarySearch(forLastIndexOf: value))
print(array.binarySearch(forIndicesRangeOf: value))
Tests
protocol _BinarySearchTestable: class where Collection: RandomAccessCollection, Collection.Element: Comparable {
associatedtype Collection
var array: Collection! { get set }
var elementToSearch: Collection.Element! { get set }
func testFindFirstIndexOfValueInCollection()
func testFindLastIndexOfValueInCollection()
func testFindIndicesRangeOfValueInCollection()
}
extension _BinarySearchTestable where Self: XCTest {
typealias Element = Collection.Element
typealias Index = Collection.Index
func _testFindFirstIndexOfValueInCollection() {
_testfindFirstIndex(comparableArray: array, testableArray: array)
}
func _testFindLastIndexOfValueInCollection() {
let index1 = array.lastIndex(of: elementToSearch)
let index2 = array.binarySearch(forLastIndexOf: elementToSearch)
_testElementsAreEqual(indexInComparableArray: index1, comparableArray: array,
indexInTestableArray: index2, testableArray: array)
}
func _testFindIndicesRangeOfValueInCollection() {
var range1: Range<Index>?
if let firstIndex = array.firstIndex(of: elementToSearch),
let lastIndex = array.lastIndex(of: elementToSearch) {
range1 = firstIndex ..< array.index(after: lastIndex)
}
let range2 = array.binarySearch(forIndicesRangeOf: elementToSearch)
XCTAssertEqual(range1, range2)
}
private func _testElementsAreEqual(indexInComparableArray: Index?, comparableArray: Collection,
indexInTestableArray: Index?, testableArray: Collection) {
XCTAssertEqual(indexInComparableArray, indexInTestableArray)
var valueInComparableArray: Element?
if let index = indexInComparableArray { valueInComparableArray = comparableArray[index] }
var valueInTestableArray: Element?
if let index = indexInComparableArray { valueInTestableArray = testableArray[index] }
XCTAssertEqual(valueInComparableArray, valueInTestableArray)
}
private func _testfindFirstIndex(comparableArray: Collection, testableArray: Collection) {
let index1 = comparableArray.firstIndex(of: elementToSearch)
let index2 = testableArray.binarySearch(forFirstIndexOf: elementToSearch)
_testElementsAreEqual(indexInComparableArray: index1, comparableArray: comparableArray,
indexInTestableArray: index2, testableArray: testableArray)
}
}
class TestsInEmptyArray: XCTestCase, _BinarySearchTestable {
var array: [String]!
var elementToSearch: String!
override func setUp() {
array = []
elementToSearch = "value"
}
func testFindFirstIndexOfValueInCollection() { _testFindFirstIndexOfValueInCollection() }
func testFindLastIndexOfValueInCollection() { _testFindLastIndexOfValueInCollection() }
func testFindIndicesRangeOfValueInCollection() { _testFindIndicesRangeOfValueInCollection() }
}
class TestsInArray: XCTestCase, _BinarySearchTestable {
var array: [Int]!
var elementToSearch: Int!
override func setUp() {
array = [1, 2, 3, 3, 3, 3, 3, 4, 5, 5]
elementToSearch = 3
}
func testFindFirstIndexOfValueInCollection() { _testFindFirstIndexOfValueInCollection() }
func testFindLastIndexOfValueInCollection() { _testFindLastIndexOfValueInCollection() }
func testFindIndicesRangeOfValueInCollection() { _testFindIndicesRangeOfValueInCollection() }
}
class TestsInArrayWithOneElement: XCTestCase, _BinarySearchTestable {
var array: [Date]!
var elementToSearch: Date!
override func setUp() {
let date = Date()
array = [date]
elementToSearch = date
}
func testFindFirstIndexOfValueInCollection() { _testFindFirstIndexOfValueInCollection() }
func testFindLastIndexOfValueInCollection() { _testFindLastIndexOfValueInCollection() }
func testFindIndicesRangeOfValueInCollection() { _testFindIndicesRangeOfValueInCollection() }
}

Related

Swift 5, Array of custom object, lowest value. Not equal to other values in same array

How do you determine if this array has only a single lowest value?
let scoresExampleOne = [2, 2, 3, 4] // return false
let scoresExampleTwo = [2, 3, 3, 5] // return true
"scoreValues" are embedded in a custom "Player" object.
I just tried to simplify it for the sake of this question.
All you need is to iterate your collection and keep track of the minimum value and if it repeats or not:
extension Collection {
func minElement<T: Comparable>(_ predicate: (Element) -> T) -> (element: Element, single: Bool)? {
guard var minElement = first else { return nil }
var min = predicate(minElement)
var single = true
for element in dropFirst() {
let value = predicate(element)
if value > min { continue }
if value < min {
minElement = element
min = value
single = true
} else {
single = false
}
}
return (minElement, single)
}
func min<T: Comparable>(_ predicate: (Element) -> T) -> (min: T, single: Bool)? {
guard let (element, single) = minElement(predicate) else { return nil }
return (predicate(element), single)
}
}
Playground testing:
struct Player {
let score: Int
}
let players1: [Player] = [.init(score: 2),
.init(score: 2),
.init(score: 3),
.init(score: 4)]
let players2: [Player] = [.init(score: 2),
.init(score: 3),
.init(score: 3),
.init(score: 5)]
let scoresExampleOne = players1.min(\.score) // (min 2, single false)
let scoresExampleTwo = players2.min(\.score) // (min 2, single true)
let scoresExampleThree = players1.minElement(\.score) // ({score 2}, single false)
let scoresExampleFour = players2.minElement(\.score) // ({score 2}, single true)
func isSingleLowestValue(scores: [Int]) -> Bool {
guard let min = scores.min() else { return false }
let minCount = scores.lazy.filter { $0 == min }.count
return minCount == 1
}
Leo's answer is good, but it's a special case of this extremum method.
public extension Sequence {
/// The first element of the sequence.
/// - Note: `nil` if the sequence is empty.
var first: Element? {
var iterator = makeIterator()
return iterator.next()
}
/// - Parameters:
/// - comparable: The property to compare.
/// - areSorted: Whether the elements are in order, approaching the extremum.
func extremum<Comparable: Swift.Comparable>(
comparing comparable: (Element) throws -> Comparable,
areSorted: (Comparable, Comparable) throws -> Bool
) rethrows -> Extremum<Element>? {
try first.map { first in
try dropFirst().reduce(into: .init(value: first, count: 1)) {
let comparables = (try comparable($0.value), try comparable($1))
if try areSorted(comparables.0, comparables.1) {
$0 = .init(value: $1, count: 1)
} else if (comparables.0 == comparables.1) {
$0.count += 1
}
}
}
}
/// - throws: `Extremum<Element>.UniqueError`
func uniqueMin<Comparable: Swift.Comparable>(
comparing comparable: (Element) throws -> Comparable
) throws -> Extremum<Element> {
typealias Error = Extremum<Element>.UniqueError
guard let extremum = try extremum(comparing: comparable, areSorted: >)
else { throw Error.emptySequence }
guard extremum.count == 1
else { throw Error.notUnique(extremum) }
return extremum
}
}
public struct Extremum<Value> {
enum UniqueError: Swift.Error {
case emptySequence
case notUnique(Extremum)
}
var value: Value
var count: Int
}

How to compare each element of array to other elements?

I need to find pairs in array, i wanted to do it with comparing arr[i] with others starting from arr[i+1] so i won't check for same value twice, but i can't get that going.
Here's what i've tried
func findPairs(_ ar: [Int]) -> Int {
var pairs = 0
for i in 0..<ar.count {
var k = i + 1
for k in 0..<ar.count {
if ar[i] == ar[k] {
pairs += 1
}
}
}
return pairs/2
}
Upd
to clarify, in the example i have, it was given as in array [1,2,3,1,2,3,4,5,1] we have 3 pairs [1,1] [2,2] [3,3]
You probably need to use i + 1 as the starting index in your inner loop, like that:
func findPairs(_ ar: [Int]) -> Int {
var pairs = 0
for i in 0..<ar.count {
let m = i + 1
for k in m..<ar.count {
if ar[i] == ar[k] {
pairs += 1
}
}
}
return pairs
}
Also, you can use another array to store the indexes of the pairs so that you do not use the same element for another pair:
func findPairs(_ ar: [Int]) -> Int {
var pairs = 0
var pairIndexes = [Int]()
for i in 0..<ar.count where !pairIndexes.contains(i) {
let m = i + 1
for k in m..<ar.count where !pairIndexes.contains(k) {
if ar[i] == ar[k] {
pairs += 1
pairIndexes.append(contentsOf: [i, k])
break
}
}
}
return pairs
}
The functional approach would be to get the total number of occurrences of the elements and sum half of it:
func findPairs(_ ar: [Int]) -> Int {
ar.reduce(into: [Int:Int]()) { $0[$1, default: 0] += 1 }.values.reduce(0) { $0 + $1/2 }
}
findPairs([1,2,3,1,2,3,4,5,1]) // 3
findPairs([4, 4, 4, 4, 4]) // 2
Or extending collection constraining the elements to hashable:
extension Collection where Element: Hashable {
var numberOfPairs: Int {
reduce(into: [Element:Int]()) { $0[$1, default: 0] += 1 }.values.reduce(0) { $0 + $1/2 }
}
}
[1,2,3,1,2,3,4,5,1].numberOfPairs // 3
[4, 4, 4, 4, 4].numberOfPairs // 2
I think you only need to find the number of pairs in the array.
So the following can work for you:
func findPairs(_ ar: [Int]) -> Int {
var pairs = 0
for i in 0..<ar.count {
for k in (i+1)..<ar.count {
if ar[i] == ar[k] {
pairs += 1
break
}
}
}
return pairs
}

swift - Casting an Array vs an ArraySlice using protocols

In this code snippet
protocol MyProtocol {}
extension Int: MyProtocol {}
let a: Array<MyProtocol> = Array<Int>()
let b: ArraySlice<MyProtocol> = a[...]
let c: Array<Int> = a as! Array<Int>
let d: ArraySlice<Int> = b as! ArraySlice<Int>
d warns with Cast from 'ArraySlice<MyProtocol>' to unrelated type 'ArraySlice<Int>' always fails.
Why can't a Slice be cast in the same way as the original Array? Can this snippet be modified to give the Array casting behaviour to the Slice?
This is basically due to how generic variance in Swift works.
Only few types are variant in Swift, including Array<T> and Set<T>. Most other types, and the types you define, are invariant.
Invariance means that T<A> and T<B> are unrelated types even if A and B are related.
Array<T> and Set<T> are covariant, which means that a Array<A> can be assigned to a variable of type Array<B> if A is a subtype of B. You can force it to go the other way (like you did in the third line) by using as!.
ArraySlice<T>, like many other types, is simply invariant. You need to do this to convert:
let d: ArraySlice<Int> = ArraySlice(b.map { $0 as! Int })
As an addendum answer to the correct answer by #Sweeper for people who are looking for type-flexible performant copy-by-ref arrays, I ended up rolling a solution which wraps an array in a class and exposes some of the API for an array.
Not a great solution, but it does what I need it to. Boo Apple for not keeping their APIs for this sort of thing consistent.
class ArrayReference<T>: Collection {
private(set) var array : Array<T>
init(_ encapsulating: Array<T>? = nil) {
self.array = encapsulating ?? []
}
var startIndex: Int {
get {
return array.startIndex
}
}
var endIndex: Int {
get {
return array.endIndex
}
}
var count : Int {
get {
return array.count
}
}
func index(after i: Int) -> Int {
return array.index(after: i)
}
subscript (index: Int) -> T {
get { return array[index] }
set(newValue) { array[index] = newValue }
}
func append(_ newValue: T) {
array.append(newValue)
}
func removeAll() {
array.removeAll()
}
var first: T? {
if array.count > 0 {
return array[0]
} else {
return nil
}
}
var last: T? {
if array.count > 0 {
return array[array.count - 1]
} else {
return nil
}
}
func asType<C>(_ type: C.Type) -> ArrayReference<C>? {
if let array = self.array as? Array<C> {
return ArrayReference<C>(array)
} else {
return nil
}
}
}
extension ArrayReference: Equatable where T: Equatable {
static func == (lhs: ArrayReference<T>, rhs: ArrayReference<T>) -> Bool {
if lhs.count == rhs.count {
var equal = true
for (lhs, rhs) in zip(lhs, rhs) {
equal = equal && (lhs == rhs)
}
return equal
} else {
return false
}
}
}

for loop with Array2D<Type>(columns: NumColumns, rows: NumRows) [duplicate]

How would I go about implementing a custom enumerate function that makes something like this work (Swift 2):
for ((column, row), item) in Array2D.enumerate() { ... }
In my simple Array2D struct:
struct Array2D<T> : SequenceType {
let columns: Int
let rows: Int
private var array: Array<T?>
init(columns: Int, rows: Int) {
self.columns = columns
self.rows = rows
array = Array(count: rows*columns, repeatedValue: nil)
}
subscript(column: Int, row: Int) -> T? {
get {
return array[columns*row + column]
}
set {
array[columns*row + column] = newValue
}
}
func generate() -> AnyGenerator<T?> {
var column = 0
var row = 0
return anyGenerator() {
guard row < self.rows else {
return nil
}
let item = self[column, row]
if ++column == self.columns {
column = 0
++row
}
return item
}
}
}
I couldn't find any good explanation on implementing an enumerate function in Swift
The enumerate() function in Swift returns integers starting from 0 for the first part of its tuple. Those have nothing to do with the sequence you're enumerating over. So, for instance, this won't work:
let word = "hello".characters
for (index, letter) in word.enumerate() {
print(word[index])
}
Because the indices of a characterView are String.Indexs.
So there are several ways to get what you're going for. The first is to just overload enumerate() for your struct. Again, there are a few days you could do this. First off, how about a function that uses your own generator, and uses its own logic to figure out the coordinates. This could work:
func enumerate() -> AnyGenerator<((Int, Int), T?)> {
let g = self.generate()
var coord = -1
return anyGenerator {
g.next().map { ((++coord % self.columns, coord / self.columns), $0) }
}
}
But you're duplicating code there, especially from your generate method. Seeing you're already using coordinates to return each element, why not just have your enumerate method be the default, and your generate method call on that. Something like this:
// Original generate method, now returns the coords it used
func enumerate() -> AnyGenerator<((Int, Int), T?)> {
var column = 0
var row = 0
return anyGenerator() {
guard row < self.rows else {
return nil
}
let item = self[column, row]
if ++column == self.columns {
column = 0
++row
}
return ((column, row), item)
}
}
// uses enumerate, ignores coords
func generate() -> AnyGenerator<T?> {
let g = self.enumerate()
return anyGenerator {
g.next().map { $1 }
}
}
If you wanted to go a little overboard, you could write an enumerate function that enumerates the specific indices of its base. Call it specEnumerate:
public struct SpecEnumerateGen<Base : CollectionType> : GeneratorType {
private var eG: Base.Generator
private let sI: Base.Index
private var i : Base.Index?
public mutating func next() -> (Base.Index, Base.Generator.Element)? {
i?._successorInPlace() ?? {self.i = self.sI}()
return eG.next().map { (i!, $0) }
}
private init(g: Base.Generator, i: Base.Index) {
self.eG = g
self.sI = i
self.i = nil
}
}
public struct SpecEnumerateSeq<Base : CollectionType> : SequenceType {
private let col: Base
public func generate() -> SpecEnumerateGen<Base> {
return SpecEnumerateGen(g: col.generate(), i: col.startIndex)
}
}
public extension CollectionType {
func specEnumerate() -> SpecEnumerateSeq<Self> {
return SpecEnumerateSeq(col: self)
}
}
With this function, this would work:
let word = "hello".characters
for (index, letter) in word.specEnumerate() {
print(word[index])
}
But your matrix struct is still a SequenceType, with no specific indices. For that, you'll have to implement your own MatrixIndex:
public struct MatrixIndex: BidirectionalIndexType {
public let x, y : Int
private let columns: Int
public func successor() -> MatrixIndex {
return (x + 1 == columns) ?
MatrixIndex(x: 0, y: y + 1, columns: columns) :
MatrixIndex(x: x + 1, y: y, columns: columns)
}
public func predecessor() -> MatrixIndex {
return (x == 0) ?
MatrixIndex(x: columns - 1, y: y - 1, columns: columns) :
MatrixIndex(x: x - 1, y: y, columns: columns)
}
}
public func == (lhs: MatrixIndex, rhs: MatrixIndex) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
extension MatrixIndex : CustomDebugStringConvertible {
public var debugDescription: String {
return "\(x), \(y)"
}
}
extension MatrixIndex: RandomAccessIndexType {
public func advancedBy(n: Int) -> MatrixIndex {
let total = (y * columns) + x + n
return MatrixIndex(x: total % columns, y: total / columns, columns: columns)
}
public func distanceTo(other: MatrixIndex) -> Int {
return (other.x - x) + (other.y - y) * columns
}
}
Right. Now you'll need another matrix struct:
public struct Matrix2D<T> : MutableCollectionType {
public var contents: [[T]]
public subscript(index: MatrixIndex) -> T {
get {
return contents[index.y][index.x]
} set {
self.contents[index.y][index.x] = newValue
}
}
public var count: Int { return contents[0].count * contents.count }
public var startIndex: MatrixIndex {
return MatrixIndex(x: 0, y: 0, columns: contents[0].count)
}
public var endIndex: MatrixIndex {
return MatrixIndex(x: 0, y: contents.endIndex, columns: contents[0].count)
}
}
Right. So now, after all of that, this works:
let myMatrix = Matrix2D(contents: [[1, 2], [3, 4]])
for (coordinate, value) in myMatrix.specEnumerate() {
value == myMatrix[coordinate] // True every time
}
It might suffice defining your own enumerate taking advantage of the one you already have:
func enumerate() -> AnyGenerator<((Int, Int), T?)> {
var index = 0
var g = array.generate()
return anyGenerator() {
if let item = g.next() {
let column = index % self.columns
let row = index / self.columns
++index
return ((column, row) , item)
}
return nil
}
}
Notice in this case you could avoid conforming to SequenceType since I use generate from the private array. Anyway it could be consistent to do so.
Here is how then you could use it:
var a2d = Array2D<Int>(columns: 2, rows: 4)
a2d[0,1] = 4
for ((column, row), item) in a2d.enumerate() {
print ("[\(column) : \(row)] = \(item)")
}
Hope this helps

Compare 2 arrays and list the differences - Swift

I was wondering how one would go about comparing 2 boolean arrays and listing the non matching booleans.
I have written up a simple example of 2 arrays.
let array1 = [true, false, true, false]
let array2 = [true, true, true, true]
How would I compare array1 & array2 and display the non matching. I am trying to do this to check user results for a quiz game.
Thanks!
Here's one implementation, but whether it is the one you are after is completely impossible to say, because you have not specified what you think the answer should be:
let answer = zip(array1, array2).map {$0.0 == $0.1}
That gives you a list of Bool values, true if the answer matches the right answer, false if it does not.
But let's say what you wanted was a list of the indexes of those answers that are correct. Then you could say:
let answer = zip(array1, array2).enumerated().filter() {
$1.0 == $1.1
}.map{$0.0}
If you want a list of the indexes of those answers that are not correct, just change == to !=.
Xcode 11 has it supported (only available in iOS 13 or newer)
https://developer.apple.com/documentation/swift/array/3200716-difference
let oldNames = ["a", "b", "c", "d"]
let newNames = ["a", "b", "d", "e"]
let difference = newNames.difference(from: oldNames)
for change in difference {
switch change {
case let .remove(offset, oldElement, _):
print("remove:", offset, oldElement)
case let .insert(offset, newElement, _):
print("insert:", offset, newElement)
}
}
Output
remove: 2 c
insert: 3 e
the easiest thing to do is use a Set. Sets have a symmetricDifference() method that does exactly this, so you just need to convert both arrays to a set, then convert the result back into an array.
Here’s an extension to make it easier:
extension Array where Element: Hashable {
func difference(from other: [Element]) -> [Element] {
let thisSet = Set(self)
let otherSet = Set(other)
return Array(thisSet.symmetricDifference(otherSet))
} }
And here’s some example code you can use to try it out:
let names1 = ["student", "class", "teacher"]
let names2 = ["class", "teacher", "classroom"]
let difference = names1.difference(from: names2)
That will set difference to be ["student", "classroom"], because those are the two names that only appear once in either array.
I found this post looking for a solution to situation very similar to the one posted. However, I found ordered collection diffing using inferringMoves() introduced in Swift 5.1 to be suitable for my use case. In case someone else may find it useful I've added what I used:
let array1 = [true, false, true, true, false, true]
let array2 = [true, true, true, true, true, false]
let diff = array2.difference(from: array1).inferringMoves()
for change in diff {
switch change {
case .insert(let offset, let element, _):
print("insert offset \(offset) for element \(element)")
case .remove(let offset, let element, _):
print("remove offset \(offset) for element \(element)")
}
}
This prints out the values:
remove offset 4 for element false
remove offset 1 for element false
insert offset 4 for element true
insert offset 5 for element false
Ported from the Swift source code, works with Swift5, iOS 12
usage:
let difference = newArray.differenceFrom(from: originalArray) { (s1: String, s2: String) in
return s1 == s2
}
// or
let difference = Array<String>.difference(from: originalArray, to: newArray) { (s1: String, s2: String) in
return s1 == s2
}
for change in diff {
switch change {
case let .remove(offset, oldElement, _):
print("remove:", offset, oldElement)
case let .insert(offset, newElement, _):
print("insert:", offset, newElement)
}
}
Source
Array Extension
extension Array {
public func differenceFrom<C: BidirectionalCollection>(
from other: C,
by areEquivalent: (C.Element, Element) -> Bool
) -> CollectionDifference<Element>
where C.Element == Self.Element {
return Array<Element>.difference(from: other, to: self, using: areEquivalent)
}
public static func difference<C, D>(
from old: C, to new: D,
using cmp: (C.Element, D.Element) -> Bool
) -> CollectionDifference<C.Element>
where
C: BidirectionalCollection,
D: BidirectionalCollection,
C.Element == D.Element {
// Core implementation of the algorithm described at http://www.xmailserver.org/diff2.pdf
// Variable names match those used in the paper as closely as possible
func _descent(from a: UnsafeBufferPointer<C.Element>, to b: UnsafeBufferPointer<D.Element>) -> [_V] {
let n = a.count
let m = b.count
let max = n + m
var result = [_V]()
var v = _V(maxIndex: 1)
v[1] = 0
var x = 0
var y = 0
iterator: for d in 0...max {
let prev_v = v
result.append(v)
v = _V(maxIndex: d)
// The code in this loop is _very_ hot—the loop bounds increases in terms
// of the iterator of the outer loop!
for k in stride(from: -d, through: d, by: 2) {
if k == -d {
x = prev_v[k &+ 1]
} else {
let km = prev_v[k &- 1]
if k != d {
let kp = prev_v[k &+ 1]
if km < kp {
x = kp
} else {
x = km &+ 1
}
} else {
x = km &+ 1
}
}
y = x &- k
while x < n && y < m {
if !cmp(a[x], b[y]) {
break;
}
x &+= 1
y &+= 1
}
v[k] = x
if x >= n && y >= m {
break iterator
}
}
if x >= n && y >= m {
break
}
}
//_internalInvariant(x >= n && y >= m)
return result
}
// Backtrack through the trace generated by the Myers descent to produce the changes that make up the diff
func _formChanges(
from a: UnsafeBufferPointer<C.Element>,
to b: UnsafeBufferPointer<C.Element>,
using trace: [_V]
) -> [CollectionDifference<C.Element>.Change] {
var changes = [CollectionDifference<C.Element>.Change]()
changes.reserveCapacity(trace.count)
var x = a.count
var y = b.count
for d in stride(from: trace.count &- 1, to: 0, by: -1) {
let v = trace[d]
let k = x &- y
let prev_k = (k == -d || (k != d && v[k &- 1] < v[k &+ 1])) ? k &+ 1 : k &- 1
let prev_x = v[prev_k]
let prev_y = prev_x &- prev_k
while x > prev_x && y > prev_y {
// No change at this position.
x &-= 1
y &-= 1
}
//_internalInvariant((x == prev_x && y > prev_y) || (y == prev_y && x > prev_x))
if y != prev_y {
changes.append(.insert(offset: prev_y, element: b[prev_y], associatedWith: nil))
} else {
changes.append(.remove(offset: prev_x, element: a[prev_x], associatedWith: nil))
}
x = prev_x
y = prev_y
}
return changes
}
/* Splatting the collections into contiguous storage has two advantages:
*
* 1) Subscript access is much faster
* 2) Subscript index becomes Int, matching the iterator types in the algorithm
*
* Combined, these effects dramatically improves performance when
* collections differ significantly, without unduly degrading runtime when
* the parameters are very similar.
*
* In terms of memory use, the linear cost of creating a ContiguousArray (when
* necessary) is significantly less than the worst-case n² memory use of the
* descent algorithm.
*/
func _withContiguousStorage<C: Collection, R>(
for values: C,
_ body: (UnsafeBufferPointer<C.Element>) throws -> R
) rethrows -> R {
if let result = try values.withContiguousStorageIfAvailable(body) {
return result
}
let array = ContiguousArray(values)
return try array.withUnsafeBufferPointer(body)
}
return _withContiguousStorage(for: old) { a in
return _withContiguousStorage(for: new) { b in
return CollectionDifference(_formChanges(from: a, to: b, using: _descent(from: a, to: b)))!
}
}
}
}
CollectionDifference
public struct CollectionDifference<ChangeElement> {
/// A single change to a collection.
#frozen
public enum Change {
/// An insertion.
///
/// The `offset` value is the offset of the inserted element in the final
/// state of the collection after the difference is fully applied.
/// A non-`nil` `associatedWith` value is the offset of the complementary
/// change.
case insert(offset: Int, element: ChangeElement, associatedWith: Int?)
/// A removal.
///
/// The `offset` value is the offset of the element to be removed in the
/// original state of the collection. A non-`nil` `associatedWith` value is
/// the offset of the complementary change.
case remove(offset: Int, element: ChangeElement, associatedWith: Int?)
// Internal common field accessors
internal var _offset: Int {
get {
switch self {
case .insert(offset: let o, element: _, associatedWith: _):
return o
case .remove(offset: let o, element: _, associatedWith: _):
return o
}
}
}
internal var _element: ChangeElement {
get {
switch self {
case .insert(offset: _, element: let e, associatedWith: _):
return e
case .remove(offset: _, element: let e, associatedWith: _):
return e
}
}
}
internal var _associatedOffset: Int? {
get {
switch self {
case .insert(offset: _, element: _, associatedWith: let o):
return o
case .remove(offset: _, element: _, associatedWith: let o):
return o
}
}
}
}
/// The insertions contained by this difference, from lowest offset to
/// highest.
public let insertions: [Change]
/// The removals contained by this difference, from lowest offset to highest.
public let removals: [Change]
/// The public initializer calls this function to ensure that its parameter
/// meets the conditions set in its documentation.
///
/// - Parameter changes: a collection of `CollectionDifference.Change`
/// instances intended to represent a valid state transition for
/// `CollectionDifference`.
///
/// - Returns: whether the parameter meets the following criteria:
///
/// 1. All insertion offsets are unique
/// 2. All removal offsets are unique
/// 3. All associations between insertions and removals are symmetric
///
/// Complexity: O(`changes.count`)
private static func _validateChanges<Changes: Collection>(
_ changes: Changes
) -> Bool where Changes.Element == Change {
if changes.isEmpty {
return true
}
var insertAssocToOffset = Dictionary<Int, Int>()
var removeOffsetToAssoc = Dictionary<Int, Int>()
var insertOffset = Set<Int>()
var removeOffset = Set<Int>()
for change in changes {
let offset = change._offset
if offset < 0 {
return false
}
switch change {
case .remove(_, _, _):
if removeOffset.contains(offset) {
return false
}
removeOffset.insert(offset)
case .insert(_, _, _):
if insertOffset.contains(offset) {
return false
}
insertOffset.insert(offset)
}
if let assoc = change._associatedOffset {
if assoc < 0 {
return false
}
switch change {
case .remove(_, _, _):
if removeOffsetToAssoc[offset] != nil {
return false
}
removeOffsetToAssoc[offset] = assoc
case .insert(_, _, _):
if insertAssocToOffset[assoc] != nil {
return false
}
insertAssocToOffset[assoc] = offset
}
}
}
return removeOffsetToAssoc == insertAssocToOffset
}
/// Creates a new collection difference from a collection of changes.
///
/// To find the difference between two collections, use the
/// `difference(from:)` method declared on the `BidirectionalCollection`
/// protocol.
///
/// The collection of changes passed as `changes` must meet these
/// requirements:
///
/// - All insertion offsets are unique
/// - All removal offsets are unique
/// - All associations between insertions and removals are symmetric
///
/// - Parameter changes: A collection of changes that represent a transition
/// between two states.
///
/// - Complexity: O(*n* * log(*n*)), where *n* is the length of the
/// parameter.
public init?<Changes: Collection>(
_ changes: Changes
) where Changes.Element == Change {
guard CollectionDifference<ChangeElement>._validateChanges(changes) else {
return nil
}
self.init(_validatedChanges: changes)
}
/// Internal initializer for use by algorithms that cannot produce invalid
/// collections of changes. These include the Myers' diff algorithm,
/// self.inverse(), and the move inferencer.
///
/// If parameter validity cannot be guaranteed by the caller then
/// `CollectionDifference.init?(_:)` should be used instead.
///
/// - Parameter c: A valid collection of changes that represent a transition
/// between two states.
///
/// - Complexity: O(*n* * log(*n*)), where *n* is the length of the
/// parameter.
internal init<Changes: Collection>(
_validatedChanges changes: Changes
) where Changes.Element == Change {
let sortedChanges = changes.sorted { (a, b) -> Bool in
switch (a, b) {
case (.remove(_, _, _), .insert(_, _, _)):
return true
case (.insert(_, _, _), .remove(_, _, _)):
return false
default:
return a._offset < b._offset
}
}
// Find first insertion via binary search
let firstInsertIndex: Int
if sortedChanges.isEmpty {
firstInsertIndex = 0
} else {
var range = 0...sortedChanges.count
while range.lowerBound != range.upperBound {
let i = (range.lowerBound + range.upperBound) / 2
switch sortedChanges[i] {
case .insert(_, _, _):
range = range.lowerBound...i
case .remove(_, _, _):
range = (i + 1)...range.upperBound
}
}
firstInsertIndex = range.lowerBound
}
removals = Array(sortedChanges[0..<firstInsertIndex])
insertions = Array(sortedChanges[firstInsertIndex..<sortedChanges.count])
}
public func inverse() -> Self {
return CollectionDifference(_validatedChanges: self.map { c in
switch c {
case .remove(let o, let e, let a):
return .insert(offset: o, element: e, associatedWith: a)
case .insert(let o, let e, let a):
return .remove(offset: o, element: e, associatedWith: a)
}
})
}
}
CollectionDifference Extension
extension CollectionDifference: Collection {
public typealias Element = Change
/// The position of a collection difference.
#frozen
public struct Index {
// Opaque index type is isomorphic to Int
#usableFromInline
internal let _offset: Int
internal init(_offset offset: Int) {
_offset = offset
}
}
public var startIndex: Index {
return Index(_offset: 0)
}
public var endIndex: Index {
return Index(_offset: removals.count + insertions.count)
}
public func index(after index: Index) -> Index {
return Index(_offset: index._offset + 1)
}
public subscript(position: Index) -> Element {
if position._offset < removals.count {
return removals[removals.count - (position._offset + 1)]
}
return insertions[position._offset - removals.count]
}
public func index(before index: Index) -> Index {
return Index(_offset: index._offset - 1)
}
public func formIndex(_ index: inout Index, offsetBy distance: Int) {
index = Index(_offset: index._offset + distance)
}
public func distance(from start: Index, to end: Index) -> Int {
return end._offset - start._offset
}
}
extension CollectionDifference.Index: Equatable {
#inlinable
public static func ==(
lhs: CollectionDifference.Index,
rhs: CollectionDifference.Index
) -> Bool {
return lhs._offset == rhs._offset
}
}
extension CollectionDifference.Index: Comparable {
#inlinable
public static func <(
lhs: CollectionDifference.Index,
rhs: CollectionDifference.Index
) -> Bool {
return lhs._offset < rhs._offset
}
}
extension CollectionDifference.Index: Hashable {
#inlinable
public func hash(into hasher: inout Hasher) {
hasher.combine(_offset)
}
}
extension CollectionDifference.Change: Equatable where ChangeElement: Equatable {}
extension CollectionDifference: Equatable where ChangeElement: Equatable {}
extension CollectionDifference.Change: Hashable where ChangeElement: Hashable {}
extension CollectionDifference: Hashable where ChangeElement: Hashable {}
extension CollectionDifference where ChangeElement: Hashable {
/// Returns a new collection difference with associations between individual
/// elements that have been removed and inserted only once.
///
/// - Returns: A collection difference with all possible moves inferred.
///
/// - Complexity: O(*n*) where *n* is the number of collection differences.
public func inferringMoves() -> CollectionDifference<ChangeElement> {
let uniqueRemovals: [ChangeElement: Int?] = {
var result = [ChangeElement: Int?](minimumCapacity: Swift.min(removals.count, insertions.count))
for removal in removals {
let element = removal._element
if result[element] != .none {
result[element] = .some(.none)
} else {
result[element] = .some(removal._offset)
}
}
return result.filter { (_, v) -> Bool in
v != .none
}
}()
let uniqueInsertions: [ChangeElement: Int?] = {
var result = [ChangeElement: Int?](minimumCapacity: Swift.min(removals.count, insertions.count))
for insertion in insertions {
let element = insertion._element
if result[element] != .none {
result[element] = .some(.none)
} else {
result[element] = .some(insertion._offset)
}
}
return result.filter { (_, v) -> Bool in
v != .none
}
}()
return CollectionDifference(_validatedChanges: map({ (change: Change) -> Change in
switch change {
case .remove(offset: let offset, element: let element, associatedWith: _):
if uniqueRemovals[element] == nil {
return change
}
if let assoc = uniqueInsertions[element] {
return .remove(offset: offset, element: element, associatedWith: assoc)
}
case .insert(offset: let offset, element: let element, associatedWith: _):
if uniqueInsertions[element] == nil {
return change
}
if let assoc = uniqueRemovals[element] {
return .insert(offset: offset, element: element, associatedWith: assoc)
}
}
return change
}))
}
}

Resources