Skip to content

Instantly share code, notes, and snippets.

@cliss
Last active December 11, 2015 00:39
Show Gist options
  • Save cliss/4518409 to your computer and use it in GitHub Desktop.
Save cliss/4518409 to your computer and use it in GitHub Desktop.
NSArray category that extends it with abilities inspired by Microsoft LINQ. See Readme.md below. Update 13/1/13: Heavily modified as per @jamiepinkham's recommendations. Thanks!

NSArray category that extends it with abilities inspired by Microsoft LINQ

These methods allow you to:

  • Take a subset of an array
  • Take all the elements in an array that are of a certain class
  • Get a singular item/property off each object in an array
  • Get a subset (stored in a dictionary) of items off each object in an array
  • Get the first object in an array that matches a predicate, or nil if none found.
  • Get the last object in an array that matches a predicate, or nil if none found.

A couple examples:

NSArray *array = @[ @"Casey", @"Erin", @"Chris" ];

NSString *firstC = [array firstOrNil:[NSPredicate predicateWithFormat:@"SELF BEGINSWITH \"C\""]]; // firstC = @"Casey"

NSString *lastC = [array lastOrNil:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
  return [[evaluatedObject substringToIndex:1] isEqualToString:@"C"];
}]];  // lastC = @"Chris".  This could have been done using the same predicate as firstC; just showing another approach.

NSArray *selections = [array select:^id(id source) {
  return [source uppercaseString];
}];   // selections = @[ @"CASEY", @"ERIN", @"CHRIS" ]

arr = @[ @"Casey", @30, @"Erin", @29, @"Adam", @26 ];
NSArray *strings = [arr ofClass:[NSString class]];  // Strings = @[ @"Casey", @"Erin", @"Adam" ]
NSArray *numbers = [arr ofClass:[NSNumber class]];  // Numbers = @[ @30, @29, @26 ]

arr = // Some array of NSAttributedStrings
NSArray *values = [arr valueForKeys:@[ @"string", @"length" ]]
// values = @[  @{ @"length" : 5, @"string" : @"Casey" },
//              @{ @"length" : 4, @"string" : @"Erin", },
//              @{ @"length" : 5, @"string" : @"Chris" } ]  
//
// NSArray+CLinq.h
//
// Created by Casey Liss on 12/1/13.
// Copyright (c) 2013 Casey Liss. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef id(^CLSelector)(id source);
@interface NSArray (CLinq)
// Gets all the objects in the array that satisfy the predicate.
// Note you can use [NSPredicate predicateWithBlock:] to provide a block.
- (NSArray *)where:(NSPredicate *)predicate;
// Gets all objects in the array that are of the desired class
- (NSArray *)ofClass:(Class)desiredClass;
// Gets an item off each object in the array.
// For example, getting the length of every NSString in the array.
- (NSArray *)select:(CLSelector)selector;
// Gets multiple items off each object in the arrya.
// Provide it an array of NSStrings of selector names which take no parameters.
// The result is an array of NSDictionary's where the key is the name and the value is the value for that object.
- (NSArray *)valueForKeys:(NSArray *)namesOfItems;
// Gets the first object that matches the predicate, or nil if none found.
- (id)firstOrNil:(NSPredicate *)predicate;
// Gets the last object that matches the predicate, or nil if none found.
- (id)lastOrNil:(NSPredicate *)predicate;
@end
//
// NSArray+CLinq.m
//
// Created by Casey Liss on 12/1/13.
// Copyright (c) 2013 Casey Liss. All rights reserved.
//
#import <objc/message.h>
#import "NSArray+CLinq.h"
@implementation NSArray (CLinq)
- (NSArray *)where:(NSPredicate *)predicate
{
return [self filteredArrayUsingPredicate:predicate];
}
- (NSArray *)ofClass:(Class)desiredClass
{
return [self filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject isKindOfClass:desiredClass];
}]];
}
- (NSArray *)select:(CLSelector)selector
{
NSMutableArray *retVal = [[NSMutableArray alloc] initWithCapacity:self.count];
NSEnumerator *en = [self objectEnumerator];
id current = [en nextObject];
while (current)
{
id selected = selector(current);
if (selected)
{
[retVal addObject:selected];
}
current = [en nextObject];
}
return retVal;
}
- (NSArray *)valueForKeys:(NSArray *)keys
{
NSMutableArray *retVal = [[NSMutableArray alloc] initWithCapacity:self.count];
NSMutableDictionary *keysToArrays = [NSMutableDictionary dictionaryWithCapacity:keys.count];
for (NSString *key in keys)
{
[keysToArrays setValue:[self valueForKey:key] forKey:key];
}
for (int i=0; i < self.count; ++i)
{
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:keys.count];
for (NSString *key in keys)
{
[dict setValue:keysToArrays[key][i] forKey:key];
}
[retVal addObject:dict];
}
return retVal;
}
- (id)firstOrNil:(NSPredicate *)predicate
{
return [self processPredicate:predicate usingEnumerator:[self objectEnumerator]];
}
- (id)lastOrNil:(NSPredicate *)predicate
{
return [self processPredicate:predicate usingEnumerator:[self reverseObjectEnumerator]];
}
#pragma mark - Private Methods
- (id)processPredicate:(NSPredicate *)predicate usingEnumerator:(NSEnumerator *)en
{
id current = [en nextObject];
while (current)
{
if ([predicate evaluateWithObject:current])
{
return current;
}
current = [en nextObject];
}
return nil;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment