将 Linq 风格的流畅查询 API 带给 Objective-C。
本项目包含一系列的 NSArray
和 NSDictionary
方法,允许您使用流畅语法执行查询,灵感来源于 Linq。为了使用 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];
项目中的代码在标准的 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;
// )