Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Set equivalents for valid Array methods #17

Merged
merged 3 commits into from
Jul 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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