diff --git a/Classes/NSArray+ObjectiveSugar.h b/Classes/NSArray+ObjectiveSugar.h index 8391056..2ceb76b 100644 --- a/Classes/NSArray+ObjectiveSugar.h +++ b/Classes/NSArray+ObjectiveSugar.h @@ -175,6 +175,13 @@ */ - (NSArray *)sortBy:(NSString*)key; +/** + Sorts the array by mapping the values through the given block. + + @return The sorted array + */ +- (NSArray *)sortWithBlock:(id (^)(id object))block; + /** Alias for reverseObjectEnumerator.allObjects @@ -182,6 +189,35 @@ */ - (NSArray *)reverse; +/** + Find the index of the first object for which block returns YES. + + @return An index or NSNotFound if not found + */ +- (NSUInteger)indexOf:(BOOL (^)(id object))block; + +/** + Returns two arrays, the first containing elements for + which block returns true, the second containing the rest. + + @return An array containing the two arrays + */ +- (NSArray *)partition:(BOOL (^)(id object))block; + +/** + Returns an array with only the unique objects from self. + + @return An array containing the unique objects + */ +- (NSArray *)uniq; + +/** + Compares self to another array using standard NSComparisonResult results + + @return An NSComparisonResult + */ +- (NSComparisonResult)compare:(NSArray *)array; + /** Return all the objects that are in both self and `array`. Alias for Ruby's & operator diff --git a/Classes/NSArray+ObjectiveSugar.m b/Classes/NSArray+ObjectiveSugar.m index ebb863a..5fc1968 100644 --- a/Classes/NSArray+ObjectiveSugar.m +++ b/Classes/NSArray+ObjectiveSugar.m @@ -176,10 +176,56 @@ - (NSArray *)sortBy:(NSString*)key; { return [self sortedArrayUsingDescriptors:@[descriptor]]; } +- (NSArray *)sortWithBlock:(id (^)(id object))block { + NSArray *tuples = [self map:^(id object) { return @[block(object), object]; }]; + return [tuples.sort map:^id(id object) { return object[1]; }]; +} + - (NSArray *)reverse { return self.reverseObjectEnumerator.allObjects; } +- (NSUInteger)indexOf:(BOOL (^)(id object))block { + __block NSUInteger result = NSNotFound; + [self enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) { + if (block(object)) { + result = index; + *stop = YES; + } + }]; + return result; +} + +- (NSArray *)partition:(BOOL (^)(id object))block { + NSMutableArray *yes = [NSMutableArray array]; + NSMutableArray *no = [NSMutableArray array]; + for (id object in self) { + [(block(object) ? yes : no) addObject:object]; + } + return @[ yes, no ]; +} + +- (NSArray *)uniq { + return [[NSSet setWithArray:self] allObjects]; +} + +// note: this is required for sortWithBlock +- (NSComparisonResult)compare:(NSArray *)array { + for (int index = 0; index < MAX(self.count, array.count); ++index) { + if (self.count <= index) { + return NSOrderedAscending; + } + if (array.count <= index) { + return NSOrderedDescending; + } + NSComparisonResult result = [self[index] compare:array[index]]; + if (result != NSOrderedSame) { + return result; + } + } + return NSOrderedSame; +} + #pragma mark - Set operations - (NSArray *)intersectionWithArray:(NSArray *)array { diff --git a/Example/ObjectiveSugarTests/NSArrayCategoriesTests.m b/Example/ObjectiveSugarTests/NSArrayCategoriesTests.m index 33d5734..7b754eb 100644 --- a/Example/ObjectiveSugarTests/NSArrayCategoriesTests.m +++ b/Example/ObjectiveSugarTests/NSArrayCategoriesTests.m @@ -182,6 +182,27 @@ }); }); + context(@"comparing two arrays", ^{ + NSArray *empty = @[ ]; + NSArray *a = @[ @2, @3 ]; + NSArray *b = @[ @2, @4 ]; + NSArray *c = @[ @2, @3, @4 ]; + + // empty arrays + it(@"empty array == empty array", ^{ [[@([empty compare:empty]) should] equal:@(NSOrderedSame)]; }); + it(@"empty array < some array", ^{ [[@([empty compare:a]) should] equal:@(NSOrderedAscending)]; }); + it(@"some array > empty array", ^{ [[@([a compare:empty]) should] equal:@(NSOrderedDescending)]; }); + + // simple case + it(@"2,3 == 2,3", ^{ [[@([a compare:a]) should] equal:@(NSOrderedSame)]; }); + it(@"2,3 < 2,4", ^{ [[@([a compare:b]) should] equal:@(NSOrderedAscending)]; }); + it(@"2,4 > 2,3", ^{ [[@([b compare:a]) should] equal:@(NSOrderedDescending)]; }); + + // length mismatches + it(@"2,3 < 2,3,4", ^{ [[@([a compare:c]) should] equal:@(NSOrderedAscending)]; }); + it(@"2,3,4 > 2,3", ^{ [[@([c compare:a]) should] equal:@(NSOrderedDescending)]; }); + }); + context(@"sorting", ^{ it(@"-sort aliases -sortUsingComparator:", ^{ @@ -195,9 +216,46 @@ NSDictionary *dict_4 = @{@"name": @"3"}; [[[@[ dict_4, dict_1, dict_3, dict_2 ] sortBy:@"name"] should] equal:@[ dict_1, dict_2, dict_3, dict_4 ]]; }); - + + it(@"-sortWithBlock sorts with the given block", ^{ + NSArray *a = [@"the quick brown fox jumped over the lazy dog" split:@" "]; + NSArray *b = [a sortWithBlock:^id(id object) { + return @[ @([object length]), object ]; + }]; + NSLog(@"%@", b); + [[[a sortWithBlock:^id(id object) { + return @[ @([object length]), object ]; + }] should] equal: [@"dog fox the the lazy over brown quick jumped" split:@" "]]; + }); }); + context(@"-indexOf", ^{ + BOOL(^lookForSecond)(id i) = ^(id i) { + return [@"second" isEqualToString:i]; + }; + + it(@"finds an index for an element in the array", ^{ + [[@([sampleArray indexOf:lookForSecond]) should] equal:@1]; + }); + + it(@"returns NSNotFound if the element isn't present", ^{ + [[@([@[@"foo"] indexOf:lookForSecond]) should] equal:@(NSNotFound)]; + }); + }); + + it(@"-partition divides into even and odd", ^{ + NSArray *evenOdd = [oneToTen partition:^BOOL(id object) { + return [object intValue] % 2 == 0; + }]; + [[evenOdd[0] should] equal:@[@2, @4, @6, @8, @10]]; + [[evenOdd[1] should] equal:@[@1, @3, @5, @7, @9]]; + }); + + it(@"-uniq removes duplicate elements", ^{ + NSMutableArray *dups = [NSMutableArray arrayWithArray:sampleArray]; + [dups addObjectsFromArray:sampleArray]; + [[[[dups uniq] sort] should] equal:[sampleArray sort]]; + }); });