3

What's the most concise way to iterate through the indexes of an NSArray that occur before a given index? For example:

NSArray *myArray = @[ @"animal" , @"vegetable" , @"mineral" , @"piano" ];

[myArray enumerateObjectsAtIndexes:@"all before index 2" options:nil 
    usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
           // this block will be peformed on @"animal" and @"vegetable"
    }];

Also, this should not loop at all if the given index is 0.

What's the most concise, elegant way to do this? So far I've only cobbled together clumsy multi-line answers that use annoying NSRanges and index sets. Is there a better way I'm overlooking?

zakdances
  • 22,285
  • 32
  • 102
  • 173
  • Any number of ways to implement. Concise does not mean clear. Use what does the actual job well. Don't fight the framework. Iterating NSArray does not require use of that method. That method gives block usage which is great for things blocks are good at. – uchuugaka Mar 23 '13 at 12:21

4 Answers4

3
NSArray *myArray = @[ @"animal" , @"vegetable" , @"mineral" , @"piano" ];
NSUInteger stopIndex = 2;

[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if (idx == stopIndex) {
        *stop = YES; // stop enumeration
    } else {
        // Do something ...
        NSLog(@"%@", obj);
    }
}];
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • This is a good one. Is there a way to do this without the if/else? Something like *stop = (idx == stopIndex) ? (return;) : NO; – zakdances Mar 23 '13 at 12:38
  • @yourfriendzak: Not really. According to the documentation, you should only assign `YES` to `*stop`. And calling `return` from within a ternary operator is what I would call "obfuscate programming" :-) – Martin R Mar 23 '13 at 12:43
  • That requirement is due to the concurrent enumeration offered by another variant of the enumeration method. – bbum Mar 23 '13 at 13:36
3
[myArray enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, idx)]     
                           options:0
                        usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

}];
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
zakdances
  • 22,285
  • 32
  • 102
  • 173
  • 1
    Why `-1` in `NSMakeRange(-1, idx)`? Should't that be `0` ? – Martin R Mar 23 '13 at 12:32
  • 1
    @yourfriendzak. also providing nil as argument for (NSEnumerationOption) options: will give you a type cast warning. So rather then giving a nil here, it better that you should provide 0 if you want to ignore the warning. – Arslan Apr 29 '13 at 09:45
1

What about :

index = 2;
for (int i = 0; i < [myArray count] && i < index; ++i) {
   id currObj = [myArray objectAtIndex:i];
   // Do your stuff on currObj;
} 
giorashc
  • 13,691
  • 3
  • 35
  • 71
1

Personally I'd go with a block-based enumeration as shown by Martin R or yourfriendzak, the accepted answer by giorashc is probably the worst, as it doesn't provide a mutation guard.

I want to add a (correct) fast enumeration example

NSUInteger stopIndex = 2;
NSUInteger currentIndex = 0;
for (MyClass *obj in objArray) {
    if (currentIndex < stopIndex) {
        // do sth...
    } else {
        break;
    }
    ++currentIndex;      
}
Community
  • 1
  • 1
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178