1

My self.allArray contains all my objects. Then self.enabledSet contains a subset of these objects.

To create a sortedEnabledArray I currently do this:

NSArray* enabledArray = [self.enabledSet allObjects];
NSArray* sortedEnabledArray;

sortedEnabledArray = [enabledArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b)
{
    int indexA = [self.allArray indexOfObject:a];
    int indexB = [self.allArray indexOfObject:b];

    if (indexA < indexB)
    {
        return NSOrderedAscending;
    }
    else if (indexA > indexB)
    {
        return NSOrderedDescending;
    }
    else
    {
        return NSOrderedSame;
    }
}];

but I am wondering if this can't be done in a smarter/shorter way, using an NSPredicate for example. Any ideas?

EDIT: One idea to shorten is:

    int difference = indexA - indexB;

    // Convert difference to NSOrderedAscending (-1), NSOrderedSame (0), or NSOrderedDescending (1).
    return (difference != 0) ? (difference / abs(difference)) : 0;
meaning-matters
  • 21,929
  • 10
  • 82
  • 142
  • return MAX(NSOrderedAscending, MIN(indexA - indexB, NSOrderedDescending)); – LorikMalorik Feb 03 '14 at 18:01
  • 2
    Why don't you filter `self.allArray` for the enabled elements, instead of sorting the set again? – Martin R Feb 03 '14 at 18:04
  • @HussainShabbir but what should `descriptors` be? – meaning-matters Feb 03 '14 at 18:04
  • Don’t forget that there’s an [NSOrderedSet](http://nshipster.com/nsorderedset/) class in Foundation. It has its caveats (as described in that article) but it’s there if you need it. – bdesham Feb 03 '14 at 18:07
  • 1
    @meaning-matters: It seems that LorikMalorik had the same idea, you could accept his answer. – Martin R Feb 03 '14 at 18:11
  • @HussainShabbir There are no keys on which can be sorted; there is only the position in `allArray`. – meaning-matters Feb 03 '14 at 18:25
  • Are you only trying to write less lines or write better implementation? You suggestion to shorten the code is not nice, because it doesn't speed up anything and it makes the code less readable (adding the comment doesn't help). You should focus on complexity. Doing two `indexOfObject:` lookups per comparision is really inefficient. Answer by LorikMalorik addresses this issue well by using `NSSet` lookup. Or you can create `enabled` property like suggested by MartinR. – Tricertops Feb 03 '14 at 18:40
  • @iMartin No worries, better implementation (i.e., more meaningful, because 'meaning matters' ;-) the most) is always my goal. But at the moment I'm also exploring things like `NSPredicate` more, as opportunities come along while working on real code. – meaning-matters Feb 03 '14 at 18:45
  • 1
    @meaning-matters: In your comparator block, you can `return (indexA > indexB) - (indexA < indexB);`. That evaluates to `+1/0/-1` as required without using abs() or a division. Compare http://stackoverflow.com/a/20213321/1187415. – Martin R Feb 03 '14 at 18:53

3 Answers3

4

It depends on number of items in your set and array, but you can do this way:

NSPredicate* predicate = [NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary* bindings)
{
    return [self.enabledSet containsObject:object];
}];

NSArray* sortedEnabledArray = [self.allArray filteredArrayUsingPredicate:predicate];
meaning-matters
  • 21,929
  • 10
  • 82
  • 142
LorikMalorik
  • 2,001
  • 1
  • 14
  • 14
3

My suggestion is to use a different approach. There is a companion to NSArray called NSIndexSet (and it's mutable counterpart, NSMutableIndexSet). It is an object that is specifically intended to keep track of subsets of an array that meet a given criteria.

NSArray includes methods like indexesOfObjectsPassingTest (and other variants that include additional parameters.) that let you add the indexes of some members of an array to an index set.

Once you have an index set that represents a subset of your allArray, you could use a method like objectsAtIndexes to get an array of just the selected objects.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Good point, but still the conclusion is, that in `indexesOfObjectsPassingTest:` you uses `NSSet` lookup. Best solution really depends on other details we don't know. Doing `indexesOfObjectsPassingTest:` + `objectsAtIndexes:` is almost the same as doing `filteredArrayUsingPredicate:`. – Tricertops Feb 03 '14 at 18:44
  • And how would I get my sorted subset array? Note that there is no property on which I can sort. Still interesting suggestion, thanks. – meaning-matters Feb 03 '14 at 18:48
  • @meaning-matters, an index set preserves the order of the original array that was used to create it. objectsAtIndexes: will always give you an array in the same order as the source array. – Duncan C Feb 03 '14 at 18:52
  • @iMartin, as you say, the best choice very much depends on the details of the problem, which we don't know. Using indexesOfObjects+objectsAtIndexes never requires a sort. The index set preserves the order of the original list. Sorting tends to be slow on large data-sets, so avoiding a sort is a good thing - potentially a VERY good thing. – Duncan C Feb 03 '14 at 18:59
  • Your suggestion turns out to be the simplest and clearest! `NSIndexSet` is precisely what I need; it indeed fits my job description perfectly. – meaning-matters Feb 03 '14 at 19:06
2

Store in NSSet not objects, but indexes from allArray array.

Cy-4AH
  • 4,370
  • 2
  • 15
  • 22