根据对另一个 NSArray 字符串的排序,对自定义对象的 NSArray 进行排序
Posted
技术标签:
【中文标题】根据对另一个 NSArray 字符串的排序,对自定义对象的 NSArray 进行排序【英文标题】:Sort NSArray of custom objects based on sorting of another NSArray of strings 【发布时间】:2012-11-20 09:02:04 【问题描述】:我有两个 NSArray
对象,我希望它们的排序相同。一个包含NSString
对象,另一个包含自定义Attribute
对象。这是我的“关键” 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
自定义对象数组的排序。
我该怎么做?
【问题讨论】:
这似乎不是使用数组的好地方。字典或有序字典可能更合适,也更容易。 【参考方案1】:在此,我直接比较 stringOrder 中 obj1.assetID 的索引和 stringOrder 中 obj2.assetID 的索引(使用 @() 的 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 的不可见@interface 声明选择器 sortUsingComparator:”。 它适用于 10.6+ 或 ios 4.0+。您在寻找更兼容的解决方案吗? - (void)sortUsingComparator:(NSComparator)cmptr NS_AVAILABLE(10_6, 4_0); 你确定“items”真的被声明为一个 NSMutableArray(而不仅仅是一个 NSArray)吗? 这会对性能产生可怕的影响,因为在每次比较时调用indexOfObject
(线性搜索)会将排序减少到 O(n²)。你最好压缩数组并对结果进行排序,甚至创建一个从assetIds到它们在另一个数组中的索引的字典【参考方案2】:
虽然 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
【讨论】:
【参考方案3】:您可以采取几种方法。
您可以将 Attribute 对象存储在 NSDictionary 中,键是您的 stringOrder 数组中的字符串。然后,您可以获得一个排序后的键数组,并使用它来填充您用来显示它们的任何视图:
NSArray* sortedKeys = [dict keysSortedByValueUsingComparator:^(id obj1, id obj2)
return [obj1 compareTo:obj2];
另一个是您将排序顺序设置为 Attribute 对象的固有属性,因此可以直接对 Attributes 数组进行排序。如果排序顺序实际上是 Attributes 对象的固有属性,我只建议采用这种方法。如果不是,而您这样做,您最终会将演示信息存储在不属于它的位置。
这是一个例子:
NSArray* sortedAttrs = [attributes sortedArrayUsingComparator:^(id obj1, id obj2)
// Perform comparison of Attribute's, ahem, attributes
【讨论】:
那么在您的第一个示例中,字典如何知道根据我的对象的assetID 属性进行排序?【参考方案4】:这是我想出的非常有效的解决方案。有人看到这方面的性能问题吗?
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];
【讨论】:
【参考方案5】:并行处理:
结果(四核):
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 中以上是关于根据对另一个 NSArray 字符串的排序,对自定义对象的 NSArray 进行排序的主要内容,如果未能解决你的问题,请参考以下文章
使用值对 NSDictionary 的 NSArray 进行排序
如何按数组计数对嵌套 NSArray 的 NSArray 进行排序?