Skip to content

Instantly share code, notes, and snippets.

@atg
Created February 11, 2013 18:27
Show Gist options
  • Save atg/4756439 to your computer and use it in GitHub Desktop.
Save atg/4756439 to your computer and use it in GitHub Desktop.
// Created by Alex Gordon on 11/02/2013.
#import <Foundation/Foundation.h>
#pragma mark Objects
static BOOL respondsTo(id x, SEL s) {
if (!x || !s)
return NO;
return [x respondsToSelector:s];
}
static BOOL isKind(id x, Class cl) {
if (!x || !cl)
return NO;
return [x isKindOfClass:cl];
}
static BOOL eq(id a, id b) {
if (a == b) return YES;
if (a == nil || b == nil) return NO;
return [a isEqual:b];
}
static BOOL truthy(id x) {
if (!x) return NO;
if (x == [NSNull null]) return NO;
if (respondsTo(x, @selector(length)) && [x length] == 0) return NO;
if (respondsTo(x, @selector(count)) && [x count] == 0) return NO;
return YES;
}
static BOOL falsy(id x) {
return !truthy(x);
}
#pragma mark - Charsets
static NSCharacterSet* charset(NSString* str) {
if (!str)
return nil;
return [NSCharacterSet characterSetWithCharactersInString:str];
}
#pragma mark - Functions
typedef id (^Mapping)(id);
typedef BOOL (^Predicate)(id);
Mapping byFunction(id(*fptr)(id)) {
return [^ id (id x) {
return fptr(x);
} copy];
}
Predicate byPredicate(BOOL(*fptr)(id)) {
return [^ BOOL (id x) {
return fptr(x);
} copy];
}
#pragma mark - Iterables
typedef id<NSObject, NSFastEnumeration> Iter;
typedef void (^FMapAdder)(id);
typedef void (^FMapCallback)(id, FMapAdder);
// Super general array building
static NSArray* fmap(Iter it, FMapCallback callback) {
NSMutableArray* arr = [NSMutableArray array];
FMapAdder addNew = ^ (id newObject) {
if (newObject)
[arr addObject:newObject];
};
for (id x in it) {
callback(x, addNew);
}
return arr;
}
static NSArray* map(Iter it, id(^callback)(id x)) {
return fmap(it, ^(id old, FMapAdder addNew) {
addNew(callback(old));
});
}
static NSArray* filter(Iter it, BOOL(^callback)(id x)) {
return fmap(it, ^(id old, FMapAdder addNew) {
if (callback(old))
addNew(old);
});
}
static NSArray* truthies(Iter it) {
return filter(it, byPredicate(truthy));
}
static NSArray* falsies(Iter it) {
return filter(it, byPredicate(falsy));
}
static NSString* join(Iter it, NSString* str) {
if ([it isKindOfClass:[NSArray class]]) {
return [(NSArray*)it componentsJoinedByString:str];
}
else {
NSMutableArray* arr = nil;
if (respondsTo(it, @selector(count)))
arr = [[NSMutableArray alloc] initWithCapacity:[(NSArray*)it count]];
else
arr = [[NSMutableArray alloc] initWithCapacity:50];
for (id x in it) {
[arr addObject:x];
}
return [arr componentsJoinedByString:str];
}
}
#pragma mark - Strings
static long len(NSString* str) {
return [str length];
}
#pragma mark (building)
#ifndef fmt
#define fmt(...) [NSString stringWithFormat:__VA_ARGS__]
#endif
static NSString* cat(NSString* a, NSString* b) {
a = a ? a : @"";
b = b ? b : @"";
return [a stringByAppendingString:b];
}
static NSString* codepoint(uint32_t c) {
if (c == 0)
return @"";
unichar chars[2] = {0};
long length = CFStringGetSurrogatePairForLongCharacter(c, chars) ? 2 : 1;
return [NSString stringWithCharacters:chars length:length];
}
static NSString* repeated(NSString* str, long n) {
if (!str)
return nil;
if (n <= 0 || [str length] == 0)
return @"";
return [@"" stringByPaddingToLength:[str length] * n withString:str startingAtIndex:0];
}
#pragma mark (heads and tails)
BOOL hasPrefix(NSString* haystack, NSString* needle) {
if ([haystack length] < [needle length]) return NO;
if (![needle length]) return YES;
if (![haystack length]) return YES;
if (!haystack) haystack = @"";
if (!needle) needle = @"";
return [haystack hasPrefix:needle];
}
BOOL hasSuffix(NSString* haystack, NSString* needle) {
if ([haystack length] < [needle length]) return NO;
if (![needle length]) return YES;
if (![haystack length]) return YES;
if (!haystack) haystack = @"";
if (!needle) needle = @"";
return [haystack hasSuffix:needle];
}
static NSString* ltake(NSString* str, long amount) {
if (!str) return nil;
if (amount <= 0) return @"";
long n = [str length];
if (amount >= n)
return str;
return [str substringWithRange:NSMakeRange(0, amount)];
}
static NSString* ldrop(NSString* str, long amount) {
if (!str) return nil;
if (amount <= 0) return str;
long n = [str length];
if (amount >= n)
return @"";
return [str substringWithRange:NSMakeRange(amount, n - amount)];
}
static NSString* rtake(NSString* str, long amount) {
if (!str) return nil;
if (amount <= 0) return @"";
long n = [str length];
if (amount >= n)
return str;
return [str substringWithRange:NSMakeRange(n - amount, amount)];
}
static NSString* rdrop(NSString* str, long amount) {
if (!str) return nil;
if (amount <= 0) return str;
long n = [str length];
if (amount >= n)
return @"";
return [str substringWithRange:NSMakeRange(0, n - amount)];
}
#pragma (transforms)
static NSString* toUpper(NSString* str) {
return [str uppercaseString];
}
static NSString* toLower(NSString* str) {
return [str lowercaseString];
}
static NSString* replace(NSString* haystack, NSString* needle, NSString* replacement) {
if (!haystack)
return nil;
if (![haystack length])
return @"";
return [haystack stringByReplacingOccurrencesOfString:needle withString:replacement ?: @""];
}
static NSArray* split(NSString* haystack, id needle) {
if (!needle)
return nil;
if (!haystack)
return nil;
if (![haystack length])
return @[];
if ([needle isKindOfClass:[NSString class]])
return [haystack componentsSeparatedByString:(NSString *)needle];
if ([needle isKindOfClass:[NSCharacterSet class]])
return [haystack componentsSeparatedByCharactersInSet:(NSCharacterSet*)needle];
return nil;
}
#pragma mark (trimming)
static NSString* stripWhitespace(NSString* str) {
static NSCharacterSet* whitespace;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
whitespace = [NSCharacterSet characterSetWithCharactersInString:@" \t\n\r\f"];
});
return [str stringByTrimmingCharactersInSet:whitespace];
}
static NSArray* splitAndStrip(NSString* str, NSString* by) {
if (!str) return nil;
return filter(map(split(str, by), ^id(id x) {
return stripWhitespace(x);
}), ^BOOL(id x) {
return [(NSString*)x length] > 0;
});
}
static NSString* trim(NSString* str, NSCharacterSet* charset) {
return [str stringByTrimmingCharactersInSet:charset];
}
static NSString* ltrim(NSString* str, NSCharacterSet* charset) {
if (!str)
return nil;
charset = [charset invertedSet];
NSRange r = [str rangeOfCharacterFromSet:charset];
if (r.location == NSNotFound)
return @"";
if (r.location == 0)
return str;
return ldrop(str, r.location);
}
static NSString* rtrim(NSString* str, NSCharacterSet* charset) {
if (!str)
return nil;
charset = [charset invertedSet];
NSRange r = [str rangeOfCharacterFromSet:charset options:NSBackwardsSearch];
if (r.location == NSNotFound)
return @"";
if (NSMaxRange(r) == [str length])
return str;
return ltake(str, NSMaxRange(r));
}
#define TEST_STR_ABBR
#ifdef TEST_STR_ABBR
int main(int argc, const char * argv[]) { @autoreleasepool {
int successes = 0;
int failures = 0;
#define ass(x) do { BOOL b = !!(x); if (b) successes++; else { failures++; NSLog(@"Test failure on line %d: (%s) should be TRUE\n", __LINE__, #x); } } while (0)
#define nass(x) do { BOOL b = !(x); if (b) successes++; else { failures++; NSLog(@"Test failure on line %d: (%s) should be FALSE\n", __LINE__, #x); } } while (0)
#define asseq(x, y) do { BOOL b = eq(x, y); if (b) successes++; else { failures++; NSLog(@"Test failure on line %d: (%s != %s), that is [%@ != %@]\n", __LINE__, #x, #y, x, y); } } while (0)
NSArray* abcd = @[@"a", @"b", @"c", @"d"];
NSArray* ABCD = @[@"A", @"B", @"C", @"D"];
NSArray* a_c_ = @[@"a", @"", @"c", @""];
NSArray* ac = @[@"a", @"c"];
ass(!respondsTo(nil, NULL));
ass(!respondsTo(@"", NULL));
ass(!respondsTo(nil, @selector(class)));
ass(!respondsTo(@"", @selector(awijdoajwd)));
ass(respondsTo(@[], @selector(count)));
ass(!respondsTo(@"", @selector(count)));
ass(eq(@"", @""));
ass(!eq(nil, @""));
ass(!eq(@"", nil));
ass(eq(nil, nil));
ass(!eq(@"", @[]));
ass(eq(@[], @[]));
ass(!eq(@"a", @"b"));
ass(len(nil) == 0);
ass(len(@"") == 0);
ass(len(@"a") == 1);
ass(len(@"waffles") == 7);
asseq(fmt(@""), @"");
asseq(fmt(@"%d", 1), @"1");
asseq(fmt(@"%.1lf", 0.7), @"0.7");
asseq(cat(@"a", @"b"), @"ab");
asseq(cat(@"", @"b"), @"b");
asseq(cat(@"a", @""), @"a");
asseq(cat(@"", @""), @"");
asseq(cat(nil, @"b"), @"b");
asseq(cat(@"a", nil), @"a");
asseq(cat(nil, nil), @"");
asseq(codepoint('a'), @"a");
asseq(codepoint(0), @"");
asseq(repeated(@"abc", -1), @"");
asseq(repeated(@"abc", 0), @"");
asseq(repeated(@"abc", 1), @"abc");
asseq(repeated(@"abc", 4), @"abcabcabcabc");
asseq(stripWhitespace(nil), nil);
asseq(stripWhitespace(@""), @"");
asseq(stripWhitespace(@" \t \f\t\n\ra b c \t \f\t\n\r"), @"a b c");
asseq(splitAndStrip(nil, @","), nil);
asseq(splitAndStrip(@"", @","), @[]);
asseq(splitAndStrip(@" \t \f\t\n\ra \t \f\t\n\r, \t \f\t\n\r b \t \f\t\n\r, c,d \t \f\t\n\r", @","), abcd);
asseq(trim(nil, charset(@" \t")), nil);
asseq(trim(@"", charset(@" \t")), @"");
asseq(trim(@" \t a b c \t ", charset(@" \t")), @"a b c");
asseq(ltrim(nil, charset(@" \t")), nil);
asseq(ltrim(@"", charset(@" \t")), @"");
asseq(ltrim(@" \t a b c \t ", charset(@" \t")), @"a b c \t ");
asseq(ltrim(@"xyxaxbxcxyx", charset(@"xy")), @"axbxcxyx");
asseq(rtrim(nil, charset(@" \t")), nil);
asseq(rtrim(@"", charset(@" \t")), @"");
asseq(rtrim(@" \t a b c \t ", charset(@" \t")), @" \t a b c");
ass(hasPrefix(nil, nil));
ass(hasPrefix(nil, @""));
ass(!hasPrefix(nil, @"a"));
ass(hasPrefix(@"", nil));
ass(hasPrefix(@"", @""));
ass(!hasPrefix(@"", @"a"));
ass(hasPrefix(@"a", nil));
ass(hasPrefix(@"a", @""));
ass(hasPrefix(@"a", @"a"));
ass(hasPrefix(@"abc", nil));
ass(hasPrefix(@"abc", @""));
ass(hasPrefix(@"abc", @"a"));
ass(hasPrefix(@"cba", nil));
ass(hasPrefix(@"cba", @""));
ass(!hasPrefix(@"cba", @"a"));
asseq(ltake(nil, -1), nil);
asseq(ltake(nil, 0), nil);
asseq(ltake(nil, 1), nil);
asseq(ltake(@"", -1), @"");
asseq(ltake(@"", 0), @"");
asseq(ltake(@"", 1), @"");
asseq(ltake(@"a", -1), @"");
asseq(ltake(@"a", 0), @"");
asseq(ltake(@"a", 1), @"a");
asseq(ltake(@"a", 2), @"a");
asseq(ltake(@"abc", -1), @"");
asseq(ltake(@"abc", 0), @"");
asseq(ltake(@"abc", 1), @"a");
asseq(ltake(@"abc", 2), @"ab");
asseq(ltake(@"abc", 3), @"abc");
asseq(ltake(@"abc", 4), @"abc");
asseq(rtake(@"abc", -1), @"");
asseq(rtake(@"abc", 0), @"");
asseq(rtake(@"abc", 1), @"c");
asseq(rtake(@"abc", 2), @"bc");
asseq(rtake(@"abc", 3), @"abc");
asseq(rtake(@"abc", 4), @"abc");
asseq(ldrop(@"abc", -1), @"abc");
asseq(ldrop(@"abc", 0), @"abc");
asseq(ldrop(@"abc", 1), @"bc");
asseq(ldrop(@"abc", 2), @"c");
asseq(ldrop(@"abc", 3), @"");
asseq(ldrop(@"abc", 4), @"");
asseq(rdrop(@"abc", -1), @"abc");
asseq(rdrop(@"abc", 0), @"abc");
asseq(rdrop(@"abc", 1), @"ab");
asseq(rdrop(@"abc", 2), @"a");
asseq(rdrop(@"abc", 3), @"");
asseq(rdrop(@"abc", 4), @"");
asseq(toUpper(nil), nil);
asseq(toUpper(@""), @"");
asseq(toUpper(@"a"), @"A");
asseq(toUpper(@"A"), @"A");
asseq(toUpper(@"aA_"), @"AA_");
asseq(toLower(nil), nil);
asseq(toLower(@""), @"");
asseq(toLower(@"a"), @"a");
asseq(toLower(@"A"), @"a");
asseq(toLower(@"aA_"), @"aa_");
asseq(replace(nil, nil, nil), nil);
asseq(replace(@"", @"", @""), @"");
asseq(replace(@"a", @"a", @"a"), @"a");
asseq(replace(@"abcabcabc", @"b", @"X"), @"aXcaXcaXc");
asseq(replace(@"abcabcabc", @"b", @""), @"acacac");
asseq(split(nil, @","), nil);
asseq(split(@"", @","), @[]);
asseq(split(@"a,b,c,d", @","), abcd);
asseq(map(abcd, byFunction(toUpper)), ABCD);
asseq(filter(a_c_, ^ BOOL (id x) { return len(x) > 0; }), ac);
asseq(join(abcd, @""), @"abcd");
printf("\n%d failures, out of %d tests (%d%% pass rate)\n", failures, successes + failures, (int)floor(100.0 * (((double)successes) / (successes + failures))));
} return 0; }
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment