niwot0525
niwot0525

Reputation: 47

Finding cgpoint in array based only on X

I have an array of CGPoints in Swift.

I'd like to find a point in the array using only the X value (they all have unique X values, so this shouldn't be a problem) and then get the Y value from that.

I also would like a way to only see if it contains a point with the X value that I'm looking for, like a .contains that only cares about the X.

Upvotes: 1

Views: 743

Answers (2)

R Menke
R Menke

Reputation: 8411

One overly complicated but sweet way to handle problems like this is with Protocols and Extensions.

If we make a XYType protocol that has a typealias Element and x and y properties of type Element we can extend SequenceType to encapsulate the filter and contains methods.

protocol XYType {

    typealias Element

    var x : Element { get set }
    var y : Element { get set }

}

extension CGPoint : XYType {} 

Now the real methods :

Extend SequenceType but use constraints. The Generator.Element should be an XYType and the Generator.Element.Element (the Element of the XYType) should be Equatable.

The actual filter function is a bit complicated. But essentially it uses the function it gets as a parameter includeElement: (Self.Generator.Element.Element) throws -> Bool and if it is true it appends to a copy. In the end it returns that copy.

extension SequenceType where Generator.Element : XYType, Generator.Element.Element : Equatable {

    // this function just mimics the regular filter.
    func filterByX(@noescape includeElement: (Self.Generator.Element.Element) throws -> Bool) rethrows -> [Self.Generator.Element] {

        var copy : [Generator.Element] = []

        for element in self where try includeElement(element.x) {

            do {
                let include = try includeElement(element.x)
                if include {
                    copy.append(element)
                }
            } catch {
                continue
            }
        }

        return copy
    }

    func filterByY(@noescape includeElement: (Self.Generator.Element.Element) throws -> Bool) rethrows -> [Self.Generator.Element] {

        var copy : [Generator.Element] = []

        for element in self where try includeElement(element.y) {

            do {
                let include = try includeElement(element.y)
                if include {
                    copy.append(element)
                }
            } catch {
                continue
            }
        }

        return copy
    }
}

Do the same for contains

extension SequenceType where Generator.Element : XYType, Generator.Element.Element : Equatable {


    func containsX(@noescape predicate: (Self.Generator.Element.Element) throws -> Bool) rethrows -> Bool {

        for element in self {
            do {
                let result = try predicate(element.x)
                if result {
                    return true
                }
            } catch {
                continue
            }
        }

        return false
    }

    func containsY(@noescape predicate: (Self.Generator.Element.Element) throws -> Bool) rethrows -> Bool {

        for element in self {
            do {
                let result = try predicate(element.y)
                if result {
                    return true
                }
            } catch {
                continue
            }
        }

        return false
    }

}

Tests :

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

// CGPoint(2,2)
points.filterByY { (y) -> Bool in
    y == 2
}

// false
points.containsX { (x) -> Bool in
    x == 5
}

P.S. :

you can also do this, it is a little bit shorter :

let filtered = points.filter {
    $0.x == 2
}

let contains = points.contains {
    $0.x == 3
}

Upvotes: 0

tktsubota
tktsubota

Reputation: 9411

You can use the indexOf: function to find the index of an item in an array using a predicate. From there, you can access that item with the index.

This actually answers both your questions, because it either returns the index of a CGPoint with your X-value, or it returns nil.

let index = pointArray.indexOf {
    $0.x == xToFind
}

if let index = index {
    let point = pointArray[index]
    // Do something with the point that has the x-value you wanted
} else {
    // There is no point in the array with the x-value you wanted
}

Upvotes: 3

Related Questions