将 LINQ 风格的流畅查询 API 带到 Objective-C。
本项目包含一组 NSArray
和 NSDictionary
方法,允许您使用灵感来自 LINQ 的流畅语法执 accounting azion queries。要使用 Linq to Objective-C,只需将 NSArray+LinqExtensions.h
、NSArray+LinqExtensions.m
、NSDictionary+LinqExtensions.h
和 NSDictionary+LinqExtensions.m
文件复制到您的项目中,并在您想要使用 API 的任何文件中导入头文件。
或者,您可以通过 CocoaPods 包含这些文件。
作为此 API 可以执行的类型查询的示例,假设您有一个包含 Person
实例的数组,每个实例都有一个 surname
属性。下面的查询将创建来自数组的独特姓氏的排序、逗号分隔列表。
LINQSelector surnameSelector = ^id(id person){
return [person name];
};
LINQAccumulator csvAccumulator = ^id(id item, id aggregate) {
return [NSString stringWithFormat:@"%@, %@", aggregate, item];
};
NSArray* surnamesList = [[[[people linq_select:surnameSelector]
linq_sort]
linq_distinct]
linq_aggregate:csvAccumulator];
关于 LINQ 的历史及其为什么实现此 API 的详细讨论,请参见相关博客文章 http://www.scottlogic.co.uk/blog/colin/2013/02/linq-to-objective-c/。
项目中的代码以标准的 MIT 许可发布,请参阅包含的许可证文件。
NSArray
方法
NSDictionary
方法
本节提供了一些关于 API 方法的简要示例。以下示例使用了 Person 实例数组。
interface Person : NSObject
@property (retain, nonatomic) NSString* name;
@property (retain, nonatomic) NSNumber* age;
@end
- (NSArray*) linq_where:(LINQCondition)predicate;
根据谓词过滤值序列。
以下示例使用 where 方法查找 25 岁的人。
NSArray* peopleWhoAre25 = [input linq_where:^BOOL(id person) {
return [[person age] isEqualToNumber:@25];
}];
- (NSArray*) linq_select:(LINQSelector)transform;
将序列中的每个元素转换为新的形式。数组中的每个元素都通过一个'选择器'转换为新的形式,然后将新形式用于填充输出数组。
以下示例使用一个选择器,该选择器返回每个Person
实例的名称。输出将是一个包含NSString
实例的数组。
NSArray* names = [input linq_select:^id(id person) {
return [person name];
}];
- (NSArray*)linq_selectAndStopOnNil:(LINQSelector)transform;
将序列中的每个元素转换为新的形式。如果转换函数返回nil,则投影失败并返回nil。
- (NSArray*) linq_sort;
- (NSArray*) linq_sort:(LINQSelector)keySelector;
- (NSArray*) linq_sortDescending;
- (NSArray*) linq_sortDescending:(LINQSelector)keySelector;
根据它们的'自然'排序顺序或通过一个keySelector
对数组的元素进行排序。
作为自然排序的一个例子,以下示例对一组NSNumber
实例进行排序
NSArray* input = @[@21, @34, @25];
NSArray* sortedInput = [input linq_sort]; // 21, 25, 34
为了对一个包含Person
实例的数组进行排序,你可以使用键选择器
NSArray* sortedByName = [input linq_sort:^id(id person) {
return [person name];
}];
相关的'降序'方法只是颠倒排序顺序
NSArray* input = @[@21, @34, @25];
NSArray* sortedInput = [input linq_sort]; // 21, 25, 34
NSArray* sortedInput = [input linq_sortDescending]; // 34, 25, 21
- (NSNumber *)linq_sum;
计算数组中的元素之和。
- (NSArray*) linq_ofType:(Class)type;
根据指定的类型过滤数组中的元素。
在以下示例中,一个混合数组包含NSString
和NSNumber
实例,过滤后只返回NSString
实例
NSArray* mixed = @[@"foo", @25, @"bar", @33];
NSArray* strings = [mixed linq_ofType:[NSString class]];
- (NSArray*) linq_selectMany:(LINQSelector)transform;
将序列中的每个元素投影到NSArray
中,并将生成的序列扁平化为一个序列。
这是一个有趣的例子!这与select
方法类似,但是选择器必须返回一个NSArray
,select-many操作会将返回的数组扁平化为一个单一序列。
这是一个快速的示例
NSArray* data = @[@"foo, bar", @"fubar"];
NSArray* components = [data linq_selectMany:^id(id string) {
return [string componentsSeparatedByString:@", "];
}];
一个更有用的例子可能使用select-many返回一个订单数组中的所有订单行。
- (NSArray*) linq_distinct;
- (NSArray*) linq_distinct:(LINQSelector)keySelector;
从序列中返回不同的元素。这简单地将一个项目数组转换为返回一个数组,该数组包含从源顺序中唯一(即不同的)值。
此方法的零参数版本使用比较给定对象的默认方法。接受键选择器的版本允许您为每个项目指定用于相等性的值。
以下示例从一个字符串数组中返回不同的值
NSArray* names = @[@"bill", @"bob", @"bob", @"brian", @"bob"];
NSArray* distinctNames = [names linq_distinct];
// returns bill, bob and brian
以下是一个更复杂的示例,它使用键选择器找到具有不同年龄的人员实例
NSArray* peopleWithUniqueAges = [input linq_distinct:^id(id person) {
return [person age];
}];
- (id) linq_aggregate:(LINQAccumulator)accumulator;
对序列应用累计函数。此方法通过将累计函数应用到每个连续元素来将数组转换为单个值。
以下示例从一个字符串数组中创建逗号分隔的列表
NSArray* names = @[@"bill", @"bob", @"brian"];
id aggregate = [names linq_aggregate:^id(id item, id aggregate) {
return [NSString stringWithFormat:@"%@, %@", aggregate, item];
}];
// returns "bill, bob, brian"
以下示例返回一个数字数组中的最大值
NSArray* numbers = @[@22, @45, @33];
id biggestNumber = [numbers linq_aggregate:^id(id item, id aggregate) {
return [item compare:aggregate] == NSOrderedDescending ? item : aggregate;
}];
// returns 45
- (id) linq_firstOrNil;
- (id) linq_firstOrNil:(LINQCondition)predicate;
返回数组的第一个元素,如果数组为空则返回nil。
- (id) linq_lastOrNil;
返回数组的最后一个元素,如果数组为空则返回nil。
- (NSArray*) linq_skip:(NSUInteger)count;
返回一个数组,跳过源数组中的前'n'个元素,包括剩余的部分。
- (NSArray*) linq_take:(NSUInteger)count;
返回一个包含源数组前'n'个元素的数组。
- (BOOL) linq_any:(LINQCondition)condition;
测试数组中是否有任何项通过给定的条件。
例如,你可以检查数组中是否有任何数字等于25
NSArray* input = @[@25, @44, @36];
BOOL isAnyEqual = [input linq_any:^BOOL(id item) {
return [item isEqualToNumber:@25];
}];
// returns YES
- (BOOL) linq_all:(LINQCondition)condition;
测试数组中的所有项是否都通过了给定的条件。
例如,你可以检查数组中的所有数字是否都等于25
NSArray* input = @[@25, @44, @36];
BOOL areAllEqual = [input linq_all:^BOOL(id item) {
return [item isEqualToNumber:@25];
}];
// returns NO
- (NSDictionary*) linq_groupBy:(LINQSelector)groupKeySelector;
按返回的字典分组数组中的项。将对每个数组元素使用groupKeySelector
以确定它属于哪个组。
返回的字典具有作为键的组值(由键选择器返回),每个值都包含一个NSArray
,其中包含该组中的所有项。
例如,如果你想按字符串的第一个字母对一组字符串进行分组,你可以做以下操作
NSArray* input = @[@"James", @"Jim", @"Bob"];
NSDictionary* groupedByFirstLetter = [input linq_groupBy:^id(id name) {
return [name substringToIndex:1];
}];
// the returned dictionary is as follows:
// {
// J = ("James", "Jim");
// B = ("Bob");
// }
- (NSDictionary*) linq_toDictionaryWithKeySelector:(LINQSelector)keySelector;
- (NSDictionary*) linq_toDictionaryWithKeySelector:(LINQSelector)keySelector valueSelector:(LINQSelector)valueSelector;
通过将对数组中的每个项应用给定的键选择器(可选的值选择器)将源数组转换成字典。如果你使用toDictionaryWithKeySelector:
方法,或者用nil
值选择器使用带有toDictionaryWithKeySelector:valueSelector:
方法,每个字典项的值就是源数组中的项。
例如,以下代码取一个名字数组,创建一个字典,其中键是每个名字的第一个字母,值是名字(小写)。
NSArray* input = @[@"Frank", @"Jim", @"Bob"];
NSDictionary* dictionary = [input linq_toDictionaryWithKeySelector:^id(id item) {
return [item substringToIndex:1];
} valueSelector:^id(id item) {
return [item lowercaseString];
}];
// result:
// (
// F = frank;
// J = jim;
// B = bob;
// )
而在以下例子中没有值选择器,所以直接使用源数组中的字符串。
NSArray* input = @[@"Frank", @"Jim", @"Bob"];
NSDictionary* dictionary = [input linq_toDictionaryWithKeySelector:^id(id item) {
return [item substringToIndex:1];
}];
// result:
// (
// F = Frank;
// J = Jim;
// B = Bob;
// )
- (NSUInteger) linq_count:(LINQCondition)condition;
计算满足给定条件的数组中元素的数量。
例如,你可以检查有多少多个数的值相等
NSArray* input = @[@25, @35, @25];
NSUInteger numbersEqualTo25 = [input linq_count:^BOOL(id item) {
return [item isEqualToNumber:@25];
}];
// returns 2
- (NSArray*) linq_concat:(NSArray*)array;
返回一个数组,它是将给定数组连接到该数组末尾的结果。
- (NSArray*) linq_reverse;
返回一个与源数组具有相同元素但顺序相反的数组。
本节提供每个 API 方法的一些简要示例。
- (NSDictionary*) linq_where:(LINQKeyValueCondition)predicate;
根据谓语筛选字典。
以下示例使用筛选器从一个字典中删除任何键等于其值的项。
NSDictionary* result = [input linq_where:^BOOL(id key, id value) {
return [key isEqual:value];
}];
- (NSDictionary*) linq_select:(LINQKeyValueConditionKeyValueSelector)selector;
将字典中的每一对键值映射到新的形式。每个键值对通过一个'选择器'转换成新的形式,然后用于填充输出字典的值。
以下示例接受一个具有字符串值的字典,返回一个新字典,其中每个值都是源字符串的第一个字符。
NSDictionary* result = [input linq_select:^id(id key, id value) {
return [value substringToIndex:1];
}];
- (NSArray*) linq_toArray:(LINQKeyValueSelector)selector;
将字典中的每个键值对投影到一个新形式,该形式用于填充输出数组。
以下示例接受一个具有字符串值的字典,返回一个数组,该数组连接字典中每个项目的键和值。
NSDictionary* input = @{@"A" : @"Apple", @"B" : @"Banana", @"C" : @"Carrot"};
NSArray* result = [input linq_toArray:^id(id key, id value) {
return [NSString stringWithFormat:@"%@, %@", key, value];
}];
// result:
// (
// "A, Apple",
// "B, Banana",
// "C, Carrot"
// )
- (BOOL) linq_any:(LINQKeyValueCondition)condition;
测试字典中是否存在任何键值对满足给定条件。
例如,您可以检查值是否包含字母'n'。
NSDictionary* input = @{@"a" : @"apple", @"b" : @"banana", @"c" : @"bat"};
BOOL anyValuesHaveTheLetterN = [input linq_any:^BOOL(id key, id value) {
return [value rangeOfString:@"n"].length != 0;
}];
// returns YES
- (BOOL) linq_all:(LINQKeyValueCondition)condition;
测试字典中所有键值对是否满足给定条件。
例如,您可以检查所有值是否包含字母'a',或使用条件的键组件查看每个值是否包含字符串键。
NSDictionary* input = @{@"a" : @"apple", @"b" : @"banana", @"c" : @"bat"};
BOOL allValuesHaveTheLetterA = [input linq_all:^BOOL(id key, id value) {
return [value rangeOfString:@"a"].length != 0;
}];
// returns YES
BOOL allValuesContainKey = [input linq_all:^BOOL(id key, id value) {
return [value rangeOfString:key].length != 0;
}];
// returns NO - the value 'bat' does not contain the letter it is keyed with 'c'
- (NSUInteger) linq_count:(LINQKeyValueCondition)condition;
计算满足给定条件的键值对数量。
以下示例计算包含该键的字典值数量。
NSDictionary* input = @{@"a" : @"apple", @"b" : @"banana", @"c" : @"bat"};
NSUInteger valuesThatContainKey = [input linq_count:^BOOL(id key, id value) {
return [value rangeOfString:key].length != 0;
}];
// returns 2 - "bat" does not contain the key "c"
- (NSDictionary*) linq_merge:(NSDictionary*)dic;
将此字典的内容与给定的字典合并。对于任何重复项,将使用源字典的值。
以下示例合并了一对字典。
NSDictionary* input = @{@"a" : @"apple", @"b" : @"banana", @"c" : @"cat"};
NSDictionary* result = [input linq_merge:@{@"d" : @"dog", @"e" : @"elephant"}];
// result:
// (
// a = apple;
// b = banana;
// c = cat;
// d = dog;
// e = elephant;
// )