1

I have an array of objects and I want to find the duplicates. I'm comparing longitude/langitude and unfortunately the values aren't equal exactly.

How can I find the duplicates?

My example code:

var locations = [("Location_A", 49.5858, 9.123456), ("Location_B", 49.5858, 9.123456), ("Location_A", 49.5855, 9.123450), ...]

for location in locations {
    //Find duplicate based on longitude and latitude where values < 0.0004
}

In that case locations[0] and locations[2] should be detected as duplicate.

Thanks in advance!

2 Answers 2

3

Instead of tuples, use a custom struct. Now you can make that struct Equatable, defining == for that struct in such a way as to allow for your epsilon value:

struct Loc : Equatable {
    let name : String
    let latitude : Double
    let longitude : Double
    static let epsilon = 0.0004
    static func ==(lhs:Loc, rhs:Loc) -> Bool {
        if lhs.name != rhs.name { return false }
        if abs(lhs.latitude - rhs.latitude) > epsilon { return false }
        if abs(lhs.longitude - rhs.longitude) > epsilon { return false }
        return true
    }
}

Let's test it:

let loc1 = Loc(name: "Location_A", latitude: 49.5858, longitude: 9.123456)
let loc2 = Loc(name: "Location_A", latitude: 49.5855, longitude: 9.123450)
print(loc1 == loc2) // true

At that point, the well-established techniques for eliminating duplicates will spring to life.

Sign up to request clarification or add additional context in comments.

2 Comments

Hello @matt, can we override generic operator for all other struct/class? For example override operation fetch all properties of struct/class it self and compare all value.
Thank you very much! This is a very good solution and it works for me!
3

Tuples are not easy to work with, I recommend wrapping your data into a custom object first:

import CoreLocation

struct MyLocation: Hashable {
    let name: String
    let coordinate: CLLocation

    init(tuple: (String, Double, Double)) {
        name = tuple.0
        coordinate = CLLocation(latitude: tuple.1, longitude: tuple.2)
    }

    public static func == (lhs: MyLocation, rhs: MyLocation) -> Bool {
        return
            lhs.name == rhs.name
            && lhs.coordinate.distance(from: rhs.coordinate) < 1
    }

    public var hashValue: Int {
        return name.hashValue
    }
}

As you can see, I have already declared Equatable and also Hashable for easy indexing.

Then we can use a simple Array extension:

extension Array where Element: Hashable {
    func distinct() -> [Element] {
        var uniqueValues: Set<Element> = []
        return self.filter {
            let (inserted, _) = uniqueValues.insert($0)
            return inserted
        }
    }
}

And use it on our data:

var locations = [("Location_A", 49.5858, 9.123456), ("Location_B", 49.5858, 9.123456), ("Location_A", 49.5855, 9.123450)]
let myLocations = locations
    .map { MyLocation(tuple: $0) }
    .distinct()
print(myLocations)

Note that I have defined equality for two objects when they are closer than 1 meter. That will be slower than simply comparing longitude against longitude and latitude against latitude, but it will be also more precise.

1 Comment

Thanks for your help! I think this is gonna helpful for everyone!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.