Sort NSArray of custom objects based on sorting of another NSArray of strings

倖福魔咒の 提交于 2019-11-27 09:18:10

Hereby, I compare directly the index of obj1.assetID in stringOrder with the index of obj2.assetID in stringOrder (using Objective-C literals for @() to transform NSString => NSNumber)

[items sortUsingComparator:^NSComparisonResult(Attribute *obj1, Attribute *obj2) {
    return [@([stringOrder indexOfObject:obj1.assetID]) compare:@([stringOrder indexOfObject:obj2.assetID])]
}];

Or without ObjC literals :

[items sortUsingComparator:^NSComparisonResult(Attribute *obj1, Attribute *obj2) {
    return [[NSNumber numberWithInt:[stringOrder indexOfObject:obj1.assetID]] compare:[NSNumber numberWithInt:[stringOrder indexOfObject:obj2.assetID]]]
}];

While cwehrungs answer will get the job done, the performance is not great on relatively small arrays.

Here is another method for performing the same kind of sort that is a bit quicker (though still far from perfect):

NSMutableArray *sorted = [NSMutableArray array];

// pre-populate with objects
for (int i = 0; i < stringOrder.count; i++)
{
    [sorted addObject:[NSNull null]];
}
// place the items at the correct position
for (Attribute *a in items)
{
    NSUInteger idx = [stringOrder indexOfObject:a.assetID];
    if (idx != NSNotFound)
    {
        [sorted setObject:a atIndexedSubscript:idx];
    }
}
// finally remove all the unecesarry placeholders if one array was smaller
[sorted removeObject:[NSNull null]];

Comparison

Here are the results form running the two methods on an iPhone 5:

sortUsingComparator:

100  - 0.012 s
1000 - 1.116 s
2000 - 4.405 s
3000 - 9.028 s

prepopulated array

100 -  0.003 s
1000 - 0.236 s
2000 - 0.917 s
3000 - 2.063 s

There are a couple approaches you could take.

You could store your Attribute objects in an NSDictionary, with the keys being the strings in your stringOrder array. Then, you could get a sorted array of the keys and use that to populate whatever view you're using to display them:

NSArray* sortedKeys = [dict keysSortedByValueUsingComparator:^(id obj1, id obj2) {
    return [obj1 compareTo:obj2];
}

The other is that you make the sort order an intrinsic property of your Attribute object, so an array of Attributes can be sorted directly. I would only recommend taking this approach if the sort order is actually an intrinsic property of your Attributes object. If it isn't and you do this, you'll wind up storing presentation information where it doesn't belong.

Here's an example:

NSArray* sortedAttrs = [attributes sortedArrayUsingComparator:^(id obj1, id obj2) {
    // Perform comparison of Attribute's, ahem, attributes
}

Here is the solution that I came up with that works extremely well. Anyone see performance issues with this?

for (Attribute *a in items) {
    int index = [stringOrder indexOfObject:a.assetID];
    a.sortOrder = index;
}

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sortOrder" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray = [items sortedArrayUsingDescriptors:sortDescriptors];

Parallel Processing:

Results (quad core):

 1. sortme:95    sortby:852345 sorted:95    time:0.052576
 2. sortme:54248 sortby:852345 sorted:54243 time:0.264660





-(NSArray *)sortArray:(NSArray *)sortme sortBy:(NSArray *)sortBy{

CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();

NSSet *sortmeSet = [NSSet setWithArray:sortme];

NSMutableDictionary *sortDictionary = [NSMutableDictionary dictionary];
dispatch_queue_t sortDictionaryThread = dispatch_queue_create("my.sortDictionaryThread", DISPATCH_QUEUE_CONCURRENT);

[sortBy enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

    if ([sortmeSet containsObject:obj]){
        dispatch_barrier_async(sortDictionaryThread, ^{
            sortDictionary[obj] = @(idx);
        });
    }
}];


__block NSArray *sortedArray = nil;
dispatch_barrier_sync(sortDictionaryThread, ^{
    sortedArray = [sortDictionary keysSortedByValueUsingSelector:@selector(compare:)];
});

NSLog(@"sortme:%li sortby:%li sorted:%li time:%f",sortme.count,sortBy.count,sortedArray.count, CFAbsoluteTimeGetCurrent() - time);

return sortedArray;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!