47

Can you fast enumerate a NSIndexSet? if not, what's the best way to enumerate the items in the set?

Abizern
  • 129,329
  • 36
  • 198
  • 252
cfischer
  • 23,024
  • 36
  • 125
  • 209

6 Answers6

145

In OS X 10.6+ and iOS SDK 4.0+, you can use the -enumerateIndexesUsingBlock: message:

NSIndexSet *idxSet = ...

[idxSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
  //... do something with idx
  // *stop = YES; to stop iteration early
}];
Barry Wark
  • 105,416
  • 24
  • 177
  • 204
22

A while loop should do the trick. It increments the index after you use the previous index.

/*int (as commented, unreliable across different platforms)*/
NSUInteger currentIndex = [someIndexSet firstIndex];
while (currentIndex != NSNotFound)
{
    //use the currentIndex

    //increment
    currentIndex = [someIndexSet indexGreaterThanIndex: currentIndex];
}
Evan Mulawski
  • 51,888
  • 11
  • 110
  • 142
  • 2
    Valid indexes can exceed the range of values representable by `int`, since indexes are unsigned. Further, when targeting a 64-bit platform or building with `NS_BUILD_32_LIKE_64` defined, the index is a 64-bit value. Use `NSUInteger` instead of `int` in order to match the type stored by `NSIndexSet` under all platforms. – Jeremy W. Sherman Nov 17 '10 at 21:34
  • 2
    @Jeremy W. Sherman: actually the indexes are effectively limited to those values that can be represented by a **positive** `NSInteger` because the "not found" return value is `NSNotFound` which is the same as `NSIntegerMax` – JeremyP Nov 19 '10 at 15:45
  • 1
    @Evan: this example is still wrong. The comparison in the while loop needs to be against `NSNotFound` *not* -1. – JeremyP Nov 19 '10 at 15:46
  • @Evan: Interesting. I was using the Apple developer documentation as a reference. It seems a little experimentation is needed... http://developer.apple.com/library/mac/#DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSIndexSet_Class/Reference/Reference.html%23//apple_ref/occ/cl/NSIndexSet – JeremyP Nov 19 '10 at 15:55
  • @Evan Mulawski: Just verified that the Apple documentation is correct. `-indexGreaterThanIndex:` does return NSNotFound, not -1. The issue in the discussion occurs because he like you was using the wrong return type. – JeremyP Nov 19 '10 at 16:01
  • @JeremyP Wow, I never before noticed that using a signed `NSNotFound` halves the range of index values `NSUInteger` could represent if `NSNotFound` were instead `NSUIntegerMax`. I suppose `NSNotFound` is `NSIntegerMax` so as to agree with `kCFNotFound`; the `CFIndex` type used by CF collections is a signed long. @EveryoneElse Even with indices capped at `NSNotFound` (`NSIntegerMax`), `int` is still the wrong choice to store an index, because the range of `NSInteger` exceeds the range of `int` in 64-bit builds and builds with `NS_BUILD_32_LIKE_64` defined. – Jeremy W. Sherman Nov 19 '10 at 16:49
20

Fast enumeration must yield objects; since an NSIndexSet contains scalar numbers (NSUIntegers), not objects, no, you cannot fast-enumerate an index set.

Hypothetically, it could box them up into NSNumbers, but then it wouldn't be very fast.

Peter Hosey
  • 93,914
  • 14
  • 203
  • 366
10

Short answer: no. NSIndexSet does not conform to the <NSFastEnumeration> protocol.

Dave DeLong
  • 239,073
  • 58
  • 443
  • 495
2

Supposing you have an NSTableView instance (let's call it *tableView), you can delete multiple selected rows from the datasource (uhm.. *myMutableArrayDataSource), using:

[myMutableArrayDataSource removeObjectsAtIndexes:[tableView selectedRowIndexes]];

[tableView selectedRowIndexes] returns an NSIndexSet. No need to start enumerating over the indexes in the NSIndexSet yourself.

Fnord23
  • 233
  • 2
  • 18
  • This isn't actually answering the question, but rather saying "maybe you didn't need to ask that question to start with. What do you want this fast enumeration for?" – Motti Shneor Dec 12 '17 at 20:27
0

These answers are no longer true for IndexSet in Swift 5. You can perfectly get something like:

let selectedRows:IndexSet = table.selectedRowIndexes

and then enumerate the indices like this:

for index in selectedRows {
   // your code here.
}
jvarela
  • 3,153
  • 1
  • 19
  • 38