简体   繁体   English

根据C数组值对NSArray进行排序

[英]Sorting `NSArray` based on C array values

I have an NSArray with objects and a C array with doubles. 我有一个带有对象的NSArray和一个带有双打的C数组。 I would like to sort the NSArray objects based on the C array values. 我想基于C数组值对NSArray对象进行排序。

The only way I could think of is below (based on this SO question ): 我能想到的唯一方法是以下(基于此SO问题 ):

NSArray *sortedArray;
sortedArray = [origArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
{
    // Getting c array values at the same indices as the objects being compared
    NSNumber *aValue = @(cArray[[origArray indexOfObject:a]]);
    NSNumber *bValue = @(cArray[[origArray indexOfObject:b]]);
    // Returning comparison result
    return [aValue compare:bValue];
}];

But I think that the indexOfObject: part is a bit costly. 但是我认为indexOfObject:部分有点indexOfObject: Is that so? 是这样吗?

If so, is there any faster way of doing this? 如果是这样,有没有更快的方法呢?

Edit: 编辑:

To provide a bit more context, this sorting is part of a fitness evaluation for an optimization algorithm. 为了提供更多上下文,此排序是优化算法适用性评估的一部分。 In my program, I have done a basic profiling and the fitness evaluation code turns out to be the bottleneck. 在我的程序中,我已经完成了基本的性能分析,而适应性评估代码却成为了瓶颈。 As such, I am trying to optimize it. 因此,我正在尝试对其进行优化。

Also, the number of elements in the arrays is expected to be from around 100 to 10k max. 同样,阵列中的元素数量预计将在100到10k最大之间。

Don't optimize the sort until you know it's a problem. 在知道这是一个问题之前,不要优化排序。 First you need to answer some questions. 首先,您需要回答一些问题。 How many values are going to be in your array? 您的数组中将有多少个值? How long can the user be expected to wait? 用户可以等待多长时间?

After that, you should test the current solution. 之后,您应该测试当前的解决方案。 Does the user have to wait too long when sorting the expected number of values? 用户对预期数量的值进行排序时是否需要等待太久?

Next, answer other architectural questions. 接下来,回答其他架构问题。 For example, can you sort the array in the background while the user is doing other work? 例如,您可以在用户执行其他工作时在后台对数组进行排序吗?

If you do all of this and you discover a problem, then you can start to think of how to optimize. 如果您完成所有这些操作后发现问题,那么您可以开始考虑如何进行优化。

The first thing that comes to mind is mapping the arrays into a dictionary, sorting the dictionary, and mapping back the sorted array. 首先想到的是将数组映射到字典中,对字典进行排序,然后将排序后的数组映射回去。

First things first, get a mapping category (here is one I found at NSArray Equivalent of Map ). 首先,获得一个映射类别(这是我在NSArray Equivalent of Map中找到的类别)。

@interface NSArray (Map)
- (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block;
@end

@implementation NSArray (Map)
- (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block {
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:[self count]];
    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [result addObject:block(obj, idx)];
    }];
    return result;
}
@end

Once you have your mapping method of choice, then run the new sort. 选择映射方法后,请运行新的排序。

// Step 1) Map into an array of a dictionaries
NSArray *dicts = [origArray mapObjectsUsingBlock:^(id obj, NSUInteger idx) {
    return @{ @"value": obj, @"sortValue": @(cArray[idx]) };
}];

// Step 2) Sort the dictionaries
NSArray *sortedDicts = [dicts sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
{
    return [a[@"sortValue"] compare:b[@"sortValue"]];
}];

// Step 3) Map back to the sorted array
sortedArray = [sortedDicts mapObjectsUsingBlock:^(id obj, NSUInteger idx) {
    return obj[@"value"];
}];

After all of that, then you need to repeat your testing to answer the question, "Does the user have to wait too long when sorting the expected number of values?" 在完成所有这些操作之后,您需要重复测试以回答以下问题:“用户在排序期望的值数时是否需要等待太长时间?” If the answer is still yes, the user has to wait too long, then you once again need to look for a new solution. 如果答案仍然是肯定的,则用户必须等待太长时间,那么您再次需要寻找新的解决方案。

There's no sense in speculating over a potential performance problem without having a real one. 如果没有真正的问题,就无法推测潜在的性能问题。

If you can measure an actual problem with your code (eg by using Time Profiler in Instruments) you'll get an idea of where the bottle neck might be. 如果您可以用代码来衡量实际问题(例如,通过使用Instruments中的Time Profiler),您将了解瓶颈所在的位置。 In your code there are several spots that might slow the sorting down (boxing each number to calculate the NSComparisonResult , retain/release overhead from ARC just for accessing the objects from the array, ...). 在您的代码中,有几个地方可能会减慢排序速度(将每个数字装箱以计算NSComparisonResult ,保留/释放来自ARC的开销仅用于访问数组中的对象,...)。

There are a couple of ways to enhance performance but they depend upon the actual data (number of objects in origArray , source of the C array of doubles, ...). 有两种方法可以提高性能,但是它们取决于实际数据( origArray的对象数,C的double数组的源,...)。

If I'd take a guess at what might seem the candidate for performance enhancement, I'd say the general problem of having two arrays where the elements form a tuple instead of just one array of tuples should be eliminated. 如果我猜测性能增强的候选者,我会说应该消除存在两个数组的普遍问题,即元素组成一个元组而不是一个元组数组。 Here's a C function that sorts the array with a small overhead: 这是一个C函数,它以很小的开销对数组进行排序:

NSArray *sortedObjects(double valuesArray[], NSArray *objects)
{
    NSUInteger count = [objects count];
    struct tuple {
        double value;
        __unsafe_unretained id object;
    } tupleArray[count];

    for (NSUInteger i = 0; i < count; ++i)
        tupleArray[i] = (struct tuple){ valuesArray[i], objects[i] };

    qsort_b(tupleArray, count, sizeof tupleArray[0], ^int(const void *a, const void *b) {
        double v = ((const struct tuple *)a)->value - ((const struct tuple *)b)->value;
        return v == 0 ? 0 : v > 0 ? 1 : -1;
    });

    __unsafe_unretained id objectsArray[count];
    for (NSUInteger i = 0; i < count; ++i)
        objectsArray[i] = tupleArray[i].object;

    return [[NSArray alloc] initWithObjects:objectsArray count:count];
}

If so, is there any faster way of doing this? 如果是这样,有没有更快的方法呢?

Well the obvious question is why aren't the double values properties of your objects? 好吧,一个显而易见的问题是,为什么对象的双值属性没有?

If you absolutely can't have a property that is the double value (and actually, you always can by using associated objects ), you can create wrapper objects with one property that is the original object and one property that is the double value and sort an array of those. 如果您绝对不能拥有为double值的属性(实际上,您始终可以使用关联的对象 ),则可以创建包装器对象,其中一个属性为原始对象,一个属性为double值,然后进行排序这些数组。 This is, however, quite expensive (lots of allocations) and so you'll definitely want to do the profiling suggested by the other two answers first. 但是,这是相当昂贵的(很多分配),因此您一定要先进行其他两个答案所建议的分析。

If your single concern is the overhead of indexOfObject: you might want to use indexOfObjectIdenticalTo: . 如果您唯一关心的是indexOfObject:的开销, indexOfObject:可能要使用indexOfObjectIdenticalTo: It's omitting the isEqual: message to every element and might be way faster if the array has many elements. 它将isEqual:消息忽略到每个元素,并且如果数组包含许多元素,则可能会更快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM