Fast enumeration on an NSIndexSet
Asked Answered
O

6

49

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

Ornamented answered 17/11, 2010 at 20:50 Comment(0)
B
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.

Balikpapan answered 19/11, 2010 at 5:49 Comment(2)
I don't mind the performance hit, but you'd have to slow enumerate before you fast enumerate, right?Bringingup
With tagged pointers (on 64-bit platforms since OS X 10.7 and iOS 5), NSIndexSet fast enumeration ought to be pretty fast.Batfish
A
149

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
}];
Apriorism answered 17/11, 2010 at 21:20 Comment(3)
Change NSInteger to NSUIntegerDuff
This is the actual answer, can asker change?Nenitanenney
Worth noting that this is also available in iOS SDK 4.0+Ponderable
C
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];
}
Czar answered 17/11, 2010 at 20:54 Comment(6)
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.Schaub
@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 NSIntegerMaxBessiebessy
@Evan: this example is still wrong. The comparison in the while loop needs to be against NSNotFound not -1.Bessiebessy
@Evan: Interesting. I was using the Apple developer documentation as a reference. It seems a little experimentation is needed... developer.apple.com/library/mac/#DOCUMENTATION/Cocoa/Reference/…Bessiebessy
@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.Bessiebessy
@Bessiebessy 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.Schaub
B
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.

Balikpapan answered 19/11, 2010 at 5:49 Comment(2)
I don't mind the performance hit, but you'd have to slow enumerate before you fast enumerate, right?Bringingup
With tagged pointers (on 64-bit platforms since OS X 10.7 and iOS 5), NSIndexSet fast enumeration ought to be pretty fast.Batfish
G
11

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

Glamorize answered 17/11, 2010 at 21:22 Comment(1)
what about enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {} --- is this not fast enumeration?Bassist
B
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.

Boff answered 5/12, 2013 at 12:53 Comment(1)
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?"Bassist
B
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.
}
Beltane answered 8/7, 2019 at 22:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.