Andy Fedoroff Andy Fedoroff - 1 month ago 10
Swift Question

Find whether several array's elements have the same coordinates


I'm writing a Swift
extension
that checks if two or more CGPoints in array have the same coordinates. Having this code I can check all points in array.
But how to check just several elements (not all)?


Here's the extension...

import Foundation

extension Array where Element : Equatable {

func equalCoordinates() -> Bool {

if let firstElement = first {

return dropFirst().contains { $0 == firstElement }

}

return true
}
}



If two (or more) red CGPoints have identical coordinates they must be turned into green ones.


...and a code in ViewController using
equalCoordinates()
method:

func drawn() {

let colorArray = array.map { $0.pointCoord()[0] }

for dot in array {

for cPoint in dot.pointCoord() {

if colorArray.equalCoordinates() {

let altColor = dot.alternativePointColour()
draw(cPoint, color: altColor)

} else {

let color = dot.pointColour()
draw(cPoint, color: color)
}
}
}
}

...........

Swift.print(colorArray.equalCoordinates())

...........

Answer

With absolutely no concern given to efficiency (that can be improved depending on the size of your data), this is how I'd probably go about it. Each piece is pretty simple, so you should be able to adapt it to a wide variety of different outputs (if you prefer something other than IndexSet for instance).

import Foundation
import CoreGraphics

// We could put this on Collection rather than Array, but then we'd have to rewrite
// IndexSet on generic indices or use [Index].
extension Array where Element : Equatable {

    func uniqueElements() -> [Element] {
        // This is O(n^2), but it's hard to beat that without adding either 
        // Hashable (for Set) or Comparable (to pre-sort) to the requirements,
        // neither of which CGPoints have by default.
        var uniqueElements: [Element] = []

        for element in self {
            if !uniqueElements.contains(element) {
                uniqueElements.append(element)
            }
        }
        return uniqueElements
    }

    func indexSet(of element: Element) -> IndexSet {
        var indices = IndexSet()
        for (index, member) in enumerated() {
            if element == member {
                indices.insert(index)
            }
        }
        return indices
    }

    func indexSetsGroupedByEquality() -> [(element: Element, indexSet: IndexSet)] {
        return uniqueElements().map { element in (element, indexSet(of: element)) }
    }

    func indexSetsOfCollidingElements() -> [IndexSet] {
        func hasCollisions(_: Element, indexSet: IndexSet) -> Bool { return indexSet.count > 1 }

        return indexSetsGroupedByEquality()
            .filter(hasCollisions)
            .map { $0.indexSet }
    }
}

let points  = [
    CGPoint(x:1,y:1),
    CGPoint(x:2,y:1),
    CGPoint(x:1,y:1),
    CGPoint(x:3,y:1),
    CGPoint(x:2,y:1),
]

print(points.indexSetsOfCollidingElements().map(Array.init))

// [[0, 2], [1, 4]]
Comments