根据另一个字符串数组的排序,对自定义对象的NSArray进行排序

7

我有两个NSArray对象需要排序,我希望它们能按照相同的顺序排列。一个包含NSString对象,另一个包含自定义的Attribute对象。以下是我的“key” NSArray对象:

// The master order
NSArray *stringOrder = [NSArray arrayWithObjects:@"12", @"10", @"2", nil];

使用自定义对象的NSArray:

// The array of custom Attribute objects that I want sorted by the stringOrder array
NSMutableArray *items = [[NSMutableArray alloc] init];
Attribute *attribute = nil;

attribute = [[Attribute alloc] init];
attribute.assetID = @"10";
[items addObject:attribute];

attribute = [[Attribute alloc] init];
attribute.assetID = @"12";
[items addObject:attribute];

attribute = [[Attribute alloc] init];
attribute.assetID = @"2";
[items addObject:attribute];

因此,我想使用 stringOrder 数组来确定自定义对象的 items 数组的排序方式。我该如何做?


这似乎不是使用数组的好地方。使用字典或有序字典可能更合适且更容易。 - Alexander
5个回答

14

在此,我直接比较obj1.assetID在stringOrder中的索引与obj2.assetID在stringOrder中的索引(使用Objective-C字面量@()将NSString转换为NSNumber)。

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

或者不使用 ObjC 字面量:

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

当我这样做时,我只会得到一个“NSArray没有声明sortUsingComparator选择器”的错误提示。 - Nic Hubbard
它可用于10.6+或iOS 4.0+。您是否正在寻找更多兼容的解决方案?
  • (void)sortUsingComparator:(NSComparator)cmptr NS_AVAILABLE(10_6, 4_0);
- cwehrung
你确定 "items" 被声明为 NSMutableArray(而不仅仅是 NSArray)了吗? - cwehrung
2
这会带来可怕的性能影响,因为在每次比较中调用indexOfObject(线性搜索)会将排序降至O(n²)。您最好将数组压缩并对结果进行排序,甚至可以从assetIds创建一个字典,以便查找它们在其他数组中的索引。 - axelarge

6

虽然cwehrungs的答案可以完成任务,但在相对较小的数组上性能并不好。

这里有另一种执行相同类型排序的方法,速度稍快(尽管仍远非完美):

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]];

比较

以下是在 iPhone 5 上运行两种方法的结果:

sortUsingComparator:

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

预填充数组
100 -  0.003 s
1000 - 0.236 s
2000 - 0.917 s
3000 - 2.063 s

2
有几种方法可以选择。
你可以把你的 Attribute 对象存储在 NSDictionary 中,以 stringOrder 数组中的字符串作为键。然后,你可以获取已排序的键数组,并使用它来填充你用于显示这些对象的视图:
NSArray* sortedKeys = [dict keysSortedByValueUsingComparator:^(id obj1, id obj2) {
    return [obj1 compareTo:obj2];
}

另一种方法是将排序顺序作为属性对象的内在属性,这样可以直接对属性数组进行排序。如果排序顺序确实是属性对象的内在属性,则建议采用此方法。如果不是,并且你这样做了,你会把演示信息存储在不应该存在的地方。
以下是一个例子:
NSArray* sortedAttrs = [attributes sortedArrayUsingComparator:^(id obj1, id obj2) {
    // Perform comparison of Attribute's, ahem, attributes
}

在你的第一个示例中,字典如何知道基于我的对象的assetID属性进行排序? - Nic Hubbard

1

这是我想出来的解决方案,非常有效。有人发现性能问题吗?

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];

0

并行处理:

结果(四核):

 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;
}

第二个排序结果与 sortme 计数不同,因为该对象不在 sortby 中。 - ssj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接