iOS Programming Recipe 26: Using Collection Operators

Assumptions

  • You have strong knowledge of Objective-C and Core Foundation.
  • You have basic understanding of of KVC (Key Value Coding) within the Objective-C language

Many if not all developers use collection objects everyday while developing great apps, but few know about the power of collection operators. As part of KVC (key value coding), collection operators help make many routine tasks simple by eliminating and simplifying code.

Those of you still waiting on the blocks part II article, hang in there, I’ll get to it soon enough. For now I thought I would give ya’ll something short and sweet that is also really useful.

What Are Collection Operators?

Collection operators are special operators that can be passed as a key path to the KVC method


valueForKeyPath:

Each of these operators specify an action to be performed on the receiving collection. Let’s look at a quick example of using valueForKeyPath: in case you forgot how it works.

Using valueForKeyPath: we can retrieve the value of a property or sub-properties from an object by passing a string. Consider this contrived data structure.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Contrived data structure of animals written using literal notation

    @[
      @{@"name"         : @"Dog",
        @"numberOfLegs" : @4,
        @"breeds"       : @[
                             @{@"name" : @"Lab",        @"price" : @299.99},
                             @{@"name" : @"Collie",     @"price" : @199.99},
                             @{@"name" : @"Shepard",    @"price" : @299.99}
                           ]
        },
      @{@"name"         : @"Cat",
        @"numberOfLegs" : @4,
        @"breeds"       : @[
                             @{@"name" : @"Siamese",    @"price" : @99.99},
                             @{@"name" : @"Persian",    @"price" : @89.99},
                             @{@"name" : @"Himalayan",  @"price" : @79.99}
                           ]
        },
      @{@"name"         : @"Spider",
        @"numberOfLegs" : @8,
        @"breeds"       : @[
                             @{@"name" : @"Orb",        @"price" : @1.99},
                             @{@"name" : @"Widow",      @"price" : @0.99},
                             @{@"name" : @"Tarantula",  @"price" : @99.99}
                           ]
        },
      @{@"name"         : @"Fish",
        @"numberOfLegs" : @0,
        @"breeds"       : @[
                             @{@"name" : @"Trout",      @"price" : @9.89},
                             @{@"name" : @"Bass",       @"price" : @9.89},
                             @{@"name" : @"Salmon",     @"price" : @19.89},
                             @{@"name" : @"Catfish",    @"price" : @3.79}
                           ]
        },
      @{@"name"         : @"Goose",
        @"numberOfLegs" : @2,
        @"breeds"       : @[@{@"name"  : @"Canadian",   @"price" : @1099.59}]
        }
      ];

1
2
3
4
5
NSArray *animals = @[]; //shown above

NSArray *animalNames = [animals valueForKeyPath:@"name"];

// animalNames is @{@"Dog", @"Cat", @"Spider", @"Fish", @"Goose"}

Collection operators take this concept one step further by providing actions. For example what if I wanted to know the average number of legs for animals in the animals array?

Without Collection Operations


1
2
3
4
5
6
7
8
    NSArray *animals = @[]; // shown above
    NSInteger totalNumberOfLegs = 0;
    for (NSDictionary *animal in animals)
    {
        totalNumberOfLegs += [animal[@"numberOfLegs"] integerValue];
    }

    CGFloat avgNumberOfLegs = totalNumberOfLegs / (CGFloat)animals.count

With Collection Operations


1
2
NSArray *animals = @[]; // shown above
CGFloat avgNumberOfLegs = [animals valueForKeyPath:@"@avg.numberOfLegs"];

Wow! The collection operator @avg turns that into a one-liner!

Smooth Operators!

Here is the full list of collection operator defined by Apple as part of KVC.

Simple Collection Operators

Collection Operator Return Type Description
@min id Compares the values of the property specified by the key path to the right of the operator and returns the minimum value found. The minimum value is determined using the compare: method of the objects at the specified key path. The compared property objects must support comparison with each other. If the value of the right side of the key path is nil, it is ignored.
@max id Compares the values of the property specified by the key path to the right of the operator and returns the maximum value found. The maximum value is determined using the compare: method of the objects at the specified key path. The compared property objects must support comparison with each other. If the value of the right side of the key path is nil, it is ignored.
@avg NSNumber Gets values specified by the property specified by the key path to the right of the operator, converts each to a double, and returns the average value as an instance of NSNumber. In the case where the value is nil, 0 is assumed instead.
@count NSNumber Returns the number of objects in the left key path collection as an instance of NSNumber, the key path to the right of the operator is ignored.
@sum NSNumber Returns the sum of the values of the property specified by the key path to the right of the operator. Each number is converted to a double, the sum of the values is computed, and the total is wrapped as an instance of NSNumber and returned. If the value of the right side of the key path is nil, it is ignored.

Object Operators
These operators raise an exception if any of the leaf objects is nil.

Collection Operator Return Type Description
@distinctUnionOfObjects NSArray Returns an array containing the distinct objects in the property specified by the key path to the right of the operator.
@unionOfObjects NSArray Returns an array containing the distinct objects in the property specified by the key path to the right of the operator. Unlike “@distinctUnionOfObjects,” duplicate objects are not removed.

Array and Set Operators
These operators raise an exception if any of the leaf objects is nil.

Collection Operator Return Type Description
@distinctUnionOfArrays NSArray Returns an array containing the distinct objects in the property specified by the key path to the right of the operator.
@unionOfArrays NSArray Returns an array containing the objects in the property specified by the key path to the right of the operator. Unlike @distinctUnionOfArrays, duplicate objects are not removed.
@distinctUnionOfSets NSSet Returns a set containing the distinct objects in the property specified by the key path to the right of the operator.

Unfortunately the Apple docs say explicitly that custom operators cannot be defined. However, a few savvy engineers have found a way to do so (see the links at the bottom).

Another Example

Using the animals array above, suppose we want a single array of all breeds. To accomplish this we can use the @unionOfArrays operator. This operator will aggregate the breeds array from each animal in animals into a single array.


1
2
NSArray *animals = @[]; // shown above
NSArray *allBreeds = [animals valueForKeyPath:@"@unionOfArrays.breeds"];

What Now?

Many great articles have already been written on the subject of collection operators and many awesome developers have implemented some cool extensions of the concept as well. I suggest exploring the following

Using these collection operators can save a lot of time and make your code more readable. Everybody hates writing boiler plate loops and with collection operators you won’t have to!

About Joe Hoffman

I am An Electrical Engineer that spends a lot of my off time doing web development and other programming. Currently I am trying to expand my knowledge with iOS and I find that writing tutorials helps me learn more thoroughly as well as provide some useful info to others.

Speak Your Mind

*

css.php
Privacy Policy