Skip to content

Commit

Permalink
Merge pull request #17 from reTXT/master
Browse files Browse the repository at this point in the history
Add Set equivalents for valid Array methods
  • Loading branch information
mxcl authored Jul 23, 2016
2 parents cdf5fab + d147569 commit e8e481e
Show file tree
Hide file tree
Showing 40 changed files with 861 additions and 1 deletion.
2 changes: 1 addition & 1 deletion NSArray+inject.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ @implementation NSArray (YOLOInject)

- (id(^)(id, id (^)(id, id)))inject {
return ^(id initial_memo, id (^block)(id, id)) {
BOOL wasNonMutable = [initial_memo classForCoder] == [NSArray class] || [initial_memo classForCoder] == [NSDictionary class];
BOOL wasNonMutable = [initial_memo classForCoder] == [NSArray class] || [initial_memo classForCoder] == [NSDictionary class] || [initial_memo classForCoder] == [NSSet class];
if (wasNonMutable)
initial_memo = [initial_memo mutableCopy];

Expand Down
17 changes: 17 additions & 0 deletions NSDictionary+without.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#import <Foundation/NSDictionary.h>

@interface NSDictionary (YOLOWithout)

/**
Returns a new dictionary where values for objects in the given array/set/object
are removed from the receiver.
id rv = @{@1:@"1", @2:@"2", @3:@"3", @4:@"4", @5:@"5", @6:@"6"].without(@2);
// rv => @[@1:@"1", @3:@"3", @4:@"4", @5:@"5", @6:@"6"]
id rv = @[@1:@"1", @2:@"2", @3:@"3", @4:@"4", @5:@"5", @6:@"6"].without(@[@2, @3]);
// rv => @[@1:@"1", @4:@"4", @5:@"5", @6:@"6"]
*/
- (NSDictionary *(^)(id arrayOrSetOrObject))without;

@end
21 changes: 21 additions & 0 deletions NSDictionary+without.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#import "YOLO.ph"

@implementation NSDictionary (YOLOWithout)

- (NSDictionary *(^)(id))without {
return ^NSDictionary *(id arrayOrSetOrObject) {
if (!arrayOrSetOrObject)
return self;

id objs = [arrayOrSetOrObject isKindOfClass:[NSSet class]]
? [arrayOrSetOrObject allObjects]
: [arrayOrSetOrObject isKindOfClass:[NSArray class]]
? arrayOrSetOrObject
: @[arrayOrSetOrObject];
id rv = self.mutableCopy;
[rv removeObjectsForKeys:objs];
return rv;
};
}

@end
29 changes: 29 additions & 0 deletions NSSet+all.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#import <Foundation/NSSet.h>


@interface NSSet (YOLOAll)

/**
Invokes the given block for each element in the receiver. Should the
block return `NO`, the method immediately returns `NO`, ceasing
enumeration. If all executions of the block return `YES`, `all`
returns `YES`.
BOOL rv = @[@1, @2, @3].all(^(id o){
return [o intValue] > 0;
});
// rv => YES
BOOL rv = @[@1, @2, @3].all(^(int d){
return d < 3;
});
// rv => NO
Instead of a block, you can pass a `Class` object.
BOOL rv = @[@1, @2, @3].all(NSNumber.class);
// rv => YES
*/
- (BOOL(^)(id blockOrClass))all;

@end
43 changes: 43 additions & 0 deletions NSSet+all.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#import "YOLO.ph"


@implementation NSSet (YOLOAll)

- (BOOL(^)(id o))all {
return ^(id arg){
BOOL (^block)(id o) = nil;

if (arg == NSString.class /*or segfaults!*/ || YOLOIsClass(arg)) {
Class cls = arg;
block = ^(id o){
return [o isKindOfClass:cls];
};
} else {
switch ([YOLOMS(arg) getArgumentTypeAtIndex:1][0]) {
case 'c': { block = ^(id o){ return ((BOOL(^)(char))arg)([o charValue]); }; break; }
case 'i': { block = ^(id o){ return ((BOOL(^)(int))arg)([o intValue]); }; break; }
case 's': { block = ^(id o){ return ((BOOL(^)(short))arg)([o shortValue]); }; break; }
case 'l': { block = ^(id o){ return ((BOOL(^)(long))arg)([o longValue]); }; break; }
case 'q': { block = ^(id o){ return ((BOOL(^)(long long))arg)([o longLongValue]); }; break; }
case 'C': { block = ^(id o){ return ((BOOL(^)(unsigned char))arg)([o unsignedCharValue]); }; break; }
case 'I': { block = ^(id o){ return ((BOOL(^)(unsigned))arg)([o unsignedIntValue]); }; break; }
case 'S': { block = ^(id o){ return ((BOOL(^)(unsigned short))arg)([o unsignedShortValue]); }; break; }
case 'L': { block = ^(id o){ return ((BOOL(^)(unsigned long))arg)([o unsignedLongValue]); }; break; }
case 'Q': { block = ^(id o){ return ((BOOL(^)(unsigned long long))arg)([o unsignedLongLongValue]); }; break; }
case 'f': { block = ^(id o){ return ((BOOL(^)(float))arg)([o floatValue]); }; break; }
case 'd': { block = ^(id o){ return ((BOOL(^)(double))arg)([o doubleValue]); }; break; }
case 'B': { block = ^(id o){ return ((BOOL(^)(BOOL))arg)([o boolValue]); }; break; }
default:
block = arg;
break;
}
}

for (id o in self)
if (!block(o))
return NO;
return YES;
};
}

@end
18 changes: 18 additions & 0 deletions NSSet+any.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#import <Foundation/NSSet.h>

@interface NSSet (YOLOAny)

/**
BOOL rv = @[@1, @2, @3].any(^(id o){
return [o intValue] == 3;
});
// rv => YES
Instead of a block, you can pass a `Class` object.
BOOL rv = @[@1, @2, @3].any(NSNumber.class);
// rv => YES
*/
- (BOOL(^)(id blockOrClass))any;

@end
24 changes: 24 additions & 0 deletions NSSet+any.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#import "YOLO.ph"

@implementation NSSet (YOLOAny)

- (BOOL(^)(id))any {
return ^(id arg){
BOOL (^block)(id o) = nil;

if (arg == NSString.class /*or segfaults!*/ || YOLOIsClass(arg)) {
Class cls = arg;
block = ^(id o){
return [o isKindOfClass:cls];
};
} else
block = arg;

for (id o in self)
if (block(o))
return YES;
return NO;
};
}

@end
17 changes: 17 additions & 0 deletions NSSet+find.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#import <Foundation/NSSet.h>

@interface NSSet (YOLOFind)

/**
Passes each entry in the array to the given block, returning the first
element for which block is not `NO`. If no object matches, returns
`nil`.
id rv = @[@1, @2, @3, @4].find(^(id n){
return [n isEqual:@3];
});
// rv => @3
*/
- (id(^)(id))find;

@end
14 changes: 14 additions & 0 deletions NSSet+find.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#import "YOLO.ph"

@implementation NSSet (YOLOFind)

- (id(^)(id))find {
return ^id(BOOL (^block)(id o)) {
for (id item in self)
if (block(item))
return item;
return nil;
};
}

@end
30 changes: 30 additions & 0 deletions NSSet+flatMap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#import <Foundation/NSSet.h>

@interface NSSet (YOLOFlatMap)

/**
Returns a new set with the concatenated results of running block once
for every element in the receiver.
id rv = @[@1, @2, @3, @4].flatMap(^(id n){
return @[n, n];
});
// rv => @[@1, @1, @2, @2, @3, @3, @4, @4]
id rv = @[@1, @2, @3, @4].flatMap(^(id n){
return [NSSet setWithArray:@[n, n]];
});
// rv => @[@1, @1, @2, @2, @3, @3, @4, @4]
id rv = @[@1, @2, @3, @4].flatMap(^(id n){
return @[n, @[n]];
});
// rv => @[@1, @[@1], @2, @[@2], @3, @[@3], @4, @[@4]]
PROTIP: Useful over vanilla map followed by a flatten because flatten is
recursive, and you may want to preserve array relationships beyond the
first level. Also, `flatMap` is technically more efficient.
*/
- (id(^)(NSSet *(^)(id o)))flatMap;

@end
19 changes: 19 additions & 0 deletions NSSet+flatMap.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#import "YOLO.ph"

@implementation NSSet (YOLOFlatMap)

- (NSSet *(^)(NSSet *(^)(id o)))flatMap {
return ^(NSSet *(^block)(id o)){
NSMutableSet *rv = [NSMutableSet new];
for (id o in self) {
id m = block(o);
if ([m isKindOfClass:[NSSet class]])
[rv addObjectsFromArray:[m allObjects]];
else
[rv addObjectsFromArray:m];
}
return rv;
};
}

@end
14 changes: 14 additions & 0 deletions NSSet+flatten.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#import <Foundation/NSSet.h>

@interface NSSet (YOLOFlatten)

/**
Returns a new, one-dimensional array that is a recursive flattening of
the receiver.
id rv = @[@[@1, @[@2]], @3, @[@4]].flatten
// rv => @[@1, @2, @3, @4]
*/
- (NSSet *)flatten;

@end
18 changes: 18 additions & 0 deletions NSSet+flatten.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#import "YOLO.ph"

@implementation NSSet (YOLOFlatten)

- (id)flatten {
NSMutableSet *aa = [NSMutableSet set];
for (id o in self) {
if ([o isKindOfClass:[NSArray class]])
[aa addObjectsFromArray:[o flatten]];
else if ([o isKindOfClass:[NSSet class]])
[aa addObjectsFromArray:[o allObjects]];
else
[aa addObject:o];
}
return aa;
}

@end
15 changes: 15 additions & 0 deletions NSSet+groupBy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#import <Foundation/NSSet.h>

@interface NSSet (YOLOGroupBy)

/**
Groups the collection by result of the given block.
id rv = @[@1, @2, @3, @4].groupBy(^(NSNumber *n) {
return @(n.intValue % 2);
});
// rv => @{@0: @[@1, @3], @1: @[@2, @4]}
*/
- (NSDictionary *(^)(id (^)(id o)))groupBy;

@end
19 changes: 19 additions & 0 deletions NSSet+groupBy.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#import "YOLO.ph"

@implementation NSSet (YOLOGroupBy)

- (NSDictionary *(^)(id (^)(id o)))groupBy {
return ^id(id (^block)(id)) {
NSMutableDictionary *dict = [NSMutableDictionary new];
for (id o in self) {
id key = block(o);
if (!dict[key])
dict[key] = [NSMutableArray arrayWithObject:o];
else
[dict[key] addObject:o];
}
return dict;
};
}

@end
24 changes: 24 additions & 0 deletions NSSet+has.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#import <Foundation/NSArray.h>

@interface NSSet (YOLOHas)

/**
BOOL rv = @[@1, @2, @3].has(@2)
// rv => YES
Short-hand for containsObject:. Provided because doing a map, then a
select and a few more chained dot-notations are commonly followed with
the need to then determine if a particular value is in the resulting
array, and then having to square bracket the whole chain is ugly.
We decided not to override any or find with the capability to take an
object rather than a block and instead add this method. Rest assured the
decision was careful. In the end has() seemed the choice that resulted in
the clearest code.
`has` was chosen over *contains* or *includes* because it is short and
clear.
*/
- (BOOL (^)(id o))has;

@end
11 changes: 11 additions & 0 deletions NSSet+has.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import "YOLO.ph"

@implementation NSSet (YOLOHas)

- (BOOL (^)(id o))has {
return ^BOOL(id o){
return [self containsObject:o];
};
}

@end
21 changes: 21 additions & 0 deletions NSSet+inject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#import <Foundation/NSSet.h>

@interface NSSet (YOLOInject)

/**
Combines all elements of the receiver by applying a binary operation.
id rv = @[@1, @2, @3, @4].inject(@{}, ^(NSMutableDictionary *memo, NSNumber *n){
memo[n] = @(n.intValue * n.intValue);
return memo;
});
// rv => @{@1: @1, @2: @4, @3: @9, @4: @16}
PROTIP: If you feed `inject` a non-mutable dictionary or array YOLOKit
mutates it for your block, and then finally returns a non-mutable copy.
@see -reject
*/
- (id(^)(id initial_memo, id (^)(id memo, id obj)))inject;

@end
18 changes: 18 additions & 0 deletions NSSet+inject.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#import "YOLO.ph"

@implementation NSSet (YOLOInject)

- (id(^)(id, id (^)(id, id)))inject {
return ^(id initial_memo, id (^block)(id, id)) {
BOOL wasNonMutable = [initial_memo classForCoder] == [NSArray class] || [initial_memo classForCoder] == [NSDictionary class] || [initial_memo classForCoder] == [NSSet class];
if (wasNonMutable)
initial_memo = [initial_memo mutableCopy];

id memo = initial_memo;
for (id obj in self)
memo = block(memo, obj);
return wasNonMutable ? [memo copy] : memo;
};
}

@end
Loading

0 comments on commit e8e481e

Please sign in to comment.