0

I've an array from core data and I'm trying to think how can I sort the array by the nearest distance:

for (int i=0; i<allTasks.count; i++) {
        id singleTask = allTasks[i];
        double latitude = [singleTask[@"latitude"] doubleValue];
        double longitude = [singleTask[@"longitude"] doubleValue];
    }

EDIT: The distance between current location and all the locations in the array. I know how to calculate the distance, I don't know how to sort them.

Maxim Makhun
  • 2,197
  • 1
  • 22
  • 26
Idan Moshe
  • 1,675
  • 4
  • 28
  • 65

3 Answers3

2

So do you want to sort your allTasks array?

The best thing to do would be to add a distance key/value pair to each singleTask object, holding a double NSNumber.

In a first pass, loop through your allTasks array, fetch each lat/long, use it to create a CLLocation, and use the CLLocation method distanceFromLocation: to calculate the distance between each location and your target (current?) location. Save the result into each singleTask object in your array.

Once your allTasks array contains a distance property, simply use one of the sort methods like sortUsingComparator to sort the array based on the distance value. (In the sortUsingComparator family of methods, you provide a comparator block that the system uses to compare pairs of objets. It then runs a sort algorithm on your array, using your comparator to decide on the sort order.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
1

You can calculate distance between two points like this

You can also try this https://stackoverflow.com/a/9104926/3151066 and define some way of calculating distance that will satisfy you as the comparison operator

Community
  • 1
  • 1
Julian
  • 9,299
  • 5
  • 48
  • 65
  • doesn't apply because he has two fields he gotta mix to get the distance – Daij-Djan Jan 04 '14 at 13:22
  • core data can not use CoreLocation in a fetch ;) – Daij-Djan Jan 04 '14 at 13:25
  • yes but in one answer in the question I linked is described a way to sort an array in a standard way so you can define there a complex condition for determining order – Julian Jan 04 '14 at 13:26
  • so remove the first link then.. the answer is inconstinent I fear -- also we don't even know what he wants yet – Daij-Djan Jan 04 '14 at 13:27
  • @Daij-Djan edited in the way you suggested now I think my answer is consistent and should be sufficient – Julian Jan 04 '14 at 13:30
  • Calculating distance is pretty expensive mathematically. Since sorting involves comparing the objects in the array repeatedly, it's much more efficient to calculate all your distances first, then sort the array by distance as a separate step. See my post for a description of this approach. – Duncan C Jan 05 '14 at 00:57
1
  1. get the CLLocation for your currentPosition (this is done via CLLocationManager)

  2. calculate the distances for each item and store distance+item as a Pair in a Dictionary

  3. Sort Dictionary allKeys array with compare: selector

so

CLLocation *current = ...;
NSMutableDictionary *distsAndTasks [NSMutableDictionary dictionary];

for(id task in allTasks) {
    CLLocation *taskLoc = [[CLLocation alloc] initWithLatitude:task.lat longitude:task.long];//!
    CLLocationDistance dist = [taskLoc distanceFrom:current];
    if(distsAndTasks[@(dist)]) {
        NSMutableArray *equidstants = [distsAndTasks[@(dist)] mutableCopy];
        [equidstants addObject:task]; 
        distsAndTasks[@(dist)] = equidstants; 
    }
    else {
        distsAndTasks[@(dist)] = @[task]; 
    }
}

NSArray *sortedDists = [distsAndTasks.allKeys sortedArrayUsingSelector:@selector(compare:)];

//the tasks can now be access in a sorted way
for(NSNumber *dist in sortedDists) {
    NSArray *tasksAtDistance = distsAndTasks[dist];
    NSLog(@"%@", tasksAtDistance);
}
Hesham
  • 5,294
  • 3
  • 34
  • 48
Daij-Djan
  • 49,552
  • 17
  • 113
  • 135
  • disclaimer: written 100% inline, beware of typos – Daij-Djan Jan 04 '14 at 13:42
  • advice: encapsulate distance in the Task object itself. – Daij-Djan Jan 04 '14 at 13:43
  • This will fail if two or more tasks are equidistant. – CRD Jan 04 '14 at 15:42
  • fixed -- but my advice becomes even more critical. an additional property on the Task Object itself would make this MUCH easier – Daij-Djan Jan 04 '14 at 17:19
  • 1
    @Daij-Djan It may not be logical to store the distance to some other unrelated point in the Task object. Like `distanceToSomething`? Wrong. Or `distanceToCurrent`? That would need to be live-updated. No. Dictionary is good solution, but I would use **Tasks as keys** and **distances as values**, so there would be no problem with duplicates. – Tricertops Jan 05 '14 at 10:58
  • well you could have equal tasks for different distances then, no? about the distance property:, i would make distanceToCurrent and even live update it so I could use a sort descriptor – Daij-Djan Jan 05 '14 at 11:01
  • @iMartin keeping tasks as keys would mean they had to implement NSCopying... likely they do but we don't know (or I didn't) .. this will at least work now ;) [@CRD thanks] – Daij-Djan Jan 05 '14 at 11:03
  • 1
    @Daij-Djan If it doesn't support copying, then you can use some Task identifier or even its `hash`. Or `[NSMapTable strongToStrongObjectsMapTable]` :) – Tricertops Jan 05 '14 at 16:09