49

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

Abizern
  • 146,289
  • 39
  • 203
  • 257
cfischer
  • 24,452
  • 37
  • 131
  • 214

6 Answers6

148

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
  • 107,306
  • 24
  • 181
  • 206
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
  • 54,662
  • 15
  • 117
  • 144
  • 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
21

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
  • 95,783
  • 15
  • 211
  • 370
11

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

Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
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
  • 325
  • 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
1

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,744
  • 1
  • 22
  • 43