8

So I have an array of custom objects which have a Double latitude and Double longitude value. I would like to sort the array based on the calculated distance from a specific point to the location for each item in the array. I have a function that will calculate the distance based on the latitude and longitude values. Is there an easy way to accomplish this sorting?

ardevd
  • 3,329
  • 5
  • 30
  • 55

3 Answers3

30

Assuming you have a model Place for your objects:

class Place {
    var latitude: CLLocationDegrees
    var longitude: CLLocationDegrees

    var location: CLLocation {
        return CLLocation(latitude: self.latitude, longitude: self.longitude)
    }

    func distance(to location: CLLocation) -> CLLocationDistance {
        return location.distance(from: self.location)
    }
}

Then an array var places: [Place] can be sorted as such:

places.sort(by: { $0.distance(to: myLocation) < $1.distance(to: myLocation) })
xoudini
  • 7,001
  • 5
  • 23
  • 37
  • 1
    you should not safe the distance to the object. thats not good coding style. only if you need it later for caching. better in change `distance = location.distanceFromLocation(fromLocation!)` to `return distance = location.distanceFromLocation(fromLocation!)` and order by `places.sortInPlace({ $0.calculateDistance(YOUR_LOCATION) < $1..calculateDistance(YOUR_LOCATION) })` – muescha Jan 22 '17 at 17:02
  • :) i am happy to help :) – muescha Jan 23 '17 at 06:11
4

Another way to approach this problem is to make an extension on Array that allows you to sort by location. The benefit to this approach is that the call site is a lot cleaner and easier to understand.

** NOTE ** - I have changed the class to a struct. However this will work just fine with a class as well.

struct Place {
    var latitude: CLLocationDegrees
    var longitude: CLLocationDegrees

    var location: CLLocation {
        return CLLocation(latitude: self.latitude, longitude: self.longitude)
    }

    func distance(to location: CLLocation) -> CLLocationDistance {
        return location.distance(from: self.location)
    }
}

Then we declare an extension like so where the Element of the array is a Place:

extension Array where Element == Place {

    mutating func sort(by location: CLLocation) {
         return sort(by: { $0.distance(to: location) < $1.distance(to: location) })
    }

    func sorted(by location: CLLocation) -> [Place] {
        return sorted(by: { $0.distance(to: location) < $1.distance(to: location) })
    }
}

This allows us to then sort by location using the following syntaxes:

places.sort(by: myLocation) // mutating version

let newPlaces = places.sorted(by: myLocation) // non mutating version

pls
  • 1,522
  • 2
  • 21
  • 41
0

Thats quite easy to do. Your function which calculate the distance must take two parameter of type what is in your array you want to sort and return Bool, for example:

// I assumed your array stores MyLocation
func mySortFunc(location1: MyLocation, location2: MyLocation) -> Bool {

    // do your calculation here and return true or false
}

var array: [MyLocation] = ...
array.sortInPlace { (loc1, loc2) -> Bool in
    mySortFunc(loc1, location2: loc1)
}
Greg
  • 25,317
  • 6
  • 53
  • 62
  • Hmm, why does the sort function return a Bool? What is the return value determined by? This is perhaps obvious but I'm a bit confused here :) – ardevd Feb 04 '16 at 11:20
  • Let's imagine you have an array of Int [2,4,6] the sort function pass to your function 2 and 4 in first iteration and you return true if 2 is lower that 4 or false if not (example return 2<4) in the second iteration it passes 4 and 6 and so on. Your function should return true if the first element of the array is lower that the second that lets the sort function to know how to sort it. – Greg Feb 04 '16 at 11:27