Search code examples
objective-cnsarraynspredicatensrange

NSArray creating a subarray and then summing the values


I have some code that was originally written as:

+(double)averageOfArray:(NSArray*)array fromIndex:(int)from toIndex:(int)to{
    if (to < 0) return 0;
    if (to > array.count) return 0;
    double sum = 0.0;
    for (long i = from; i < to; i++){
        sum += [array[i] doubleValue];
    }
    return sum/(double)(to-from);
}

But I am trying to write it so that it's more efficient. I don't know if this will be faster or not, but one idea is to create a subarray and then calling valueForKeyPath:@"sum.self" like this:

...
NSArray *t = [array subarrayWithRange:NSMakeRange(from, to-from)];
double sum = [[t valueForKeyPath:@"sum.self"] doubleValue];
return sum/(double)(to-from);

But this is throwing an error:

Thread 1: Exception: "[<__NSCFNumber 0xd99f8c3636814118> valueForUndefinedKey:]: this class is not key value coding-compliant for the key sum."

Looking at the debugger it shows my NSArray *t as t = (__NSArrayl_Transfer *) @"500 elements". But when I look at an array that I've created like this NSArray *testArray = @[@0.1, @0.2, @0.3, @0.4 @0.5, @0.6]; then that shows up as as testArray = (__NSArrayl *) @"6 elements". I'm assuming that for some reason these underlying types are where the problem is.

I've tried creating a new array by doing this:

NSArray *t = [[NSArray alloc] initWithArray:[array subarrayWithRange:NSMakeRange(from, to-from)] copyItems:YES];`

But that does not fix the issue. What am I failing to understand?


Solution

  • Using Collection Operators:

    When a key path contains a collection operator, any portion of the key path preceding the operator, known as the left key path, indicates the collection on which to operate relative to the receiver of the message. If you send the message directly to a collection object, such as an NSArray instance, the left key path may be omitted.

    The portion of the key path after the operator, known as the right key path, specifies the property within the collection that the operator should work on. All the collection operators except @count require a right key path.

    Operator key path format

    [email protected]
    |_________________| |_________________| |_______________|
    
       Left key path         Operator        Right key path
    
    • You're sending the message directly to a collection object, you can omit the left key path.
    • Collection operator always starts with @.
    • Right key path is required (except @count).

    Because you forgot to use @ prefix, the following code ...

    double sum = [[t valueForKeyPath:@"sum.self"] doubleValue];
    

    ... tries to get sum from every NSNumber * in the array. And then you get:

    Thread 1: Exception: "[<__NSCFNumber 0xd99f8c3636814118> valueForUndefinedKey:]: this class is not key value coding-compliant for the key sum."

    Just add the @ prefix to the collection operator and you'll be fine:

    double sum = [[t valueForKeyPath:@"@sum.self"] doubleValue];