LinqToObjectiveC 2.1.0

LinqToObjectiveC 2.1.0

测试已测试
Lang语言 Obj-CObjective C
许可证 MIT
发布最后发布2016 年 2 月

Colin Eberhardt 维护。



  • 作者
  • Colin Eberhardt

Linq To Objective-C

将 LINQ 风格的流畅查询 API 带到 Objective-C。

本项目包含一组 NSArrayNSDictionary 方法,允许您使用灵感来自 LINQ 的流畅语法执 accounting azion queries。要使用 Linq to Objective-C,只需将 NSArray+LinqExtensions.hNSArray+LinqExtensions.mNSDictionary+LinqExtensions.hNSDictionary+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 许可发布,请参阅包含的许可证文件。

API 概述

NSArray 方法

NSDictionary 方法

NSArray 方法

本节提供了一些关于 API 方法的简要示例。以下示例使用了 Person 实例数组。

interface Person : NSObject

@property (retain, nonatomic) NSString* name;
@property (retain, nonatomic) NSNumber* age;

@end

linq_where

- (NSArray*) linq_where:(LINQCondition)predicate;

根据谓词过滤值序列。

以下示例使用 where 方法查找 25 岁的人。

NSArray* peopleWhoAre25 = [input linq_where:^BOOL(id person) {
    return [[person age] isEqualToNumber:@25];
}];

linq_select

- (NSArray*) linq_select:(LINQSelector)transform;

将序列中的每个元素转换为新的形式。数组中的每个元素都通过一个'选择器'转换为新的形式,然后将新形式用于填充输出数组。

以下示例使用一个选择器,该选择器返回每个Person实例的名称。输出将是一个包含NSString实例的数组。

NSArray* names = [input linq_select:^id(id person) {
    return [person name];
}];

linq_selectAndStopOnNil

- (NSArray*)linq_selectAndStopOnNil:(LINQSelector)transform;

将序列中的每个元素转换为新的形式。如果转换函数返回nil,则投影失败并返回nil。

linq_sort

- (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

linq_sum

- (NSNumber *)linq_sum;

计算数组中的元素之和。

linq_ofType

- (NSArray*) linq_ofType:(Class)type;

根据指定的类型过滤数组中的元素。

在以下示例中,一个混合数组包含NSStringNSNumber实例,过滤后只返回NSString实例

NSArray* mixed = @[@"foo", @25, @"bar", @33];
NSArray* strings = [mixed linq_ofType:[NSString class]];

linq_selectMany

- (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返回一个订单数组中的所有订单行。

linq_distinct

- (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];
}];

linq_aggregate

- (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 

linq_firstOrNil

- (id) linq_firstOrNil;
- (id) linq_firstOrNil:(LINQCondition)predicate;

返回数组的第一个元素,如果数组为空则返回nil。

linq_lastOrNil

- (id) linq_lastOrNil;

返回数组的最后一个元素,如果数组为空则返回nil。

linq_skip

- (NSArray*) linq_skip:(NSUInteger)count;

返回一个数组,跳过源数组中的前'n'个元素,包括剩余的部分。

linq_take

- (NSArray*) linq_take:(NSUInteger)count;

返回一个包含源数组前'n'个元素的数组。

linq_any

- (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

linq_all

- (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

linq_groupBy

- (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");
// }

linq_toDictionary

- (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;
// )

linq_count

- (NSUInteger) linq_count:(LINQCondition)condition;

计算满足给定条件的数组中元素的数量。

例如,你可以检查有多少多个数的值相等

NSArray* input = @[@25, @35, @25];

NSUInteger numbersEqualTo25 = [input linq_count:^BOOL(id item) {
    return [item isEqualToNumber:@25];
}];
// returns 2

linq_concat

- (NSArray*) linq_concat:(NSArray*)array;

返回一个数组,它是将给定数组连接到该数组末尾的结果。

linq_reverse

- (NSArray*) linq_reverse;

返回一个与源数组具有相同元素但顺序相反的数组。

NSDictionary 方法

本节提供每个 API 方法的一些简要示例。

linq_where

- (NSDictionary*) linq_where:(LINQKeyValueCondition)predicate;

根据谓语筛选字典。

以下示例使用筛选器从一个字典中删除任何键等于其值的项。

NSDictionary* result = [input linq_where:^BOOL(id key, id value) {
   return [key isEqual:value];
}];

linq_select

- (NSDictionary*) linq_select:(LINQKeyValueConditionKeyValueSelector)selector;

将字典中的每一对键值映射到新的形式。每个键值对通过一个'选择器'转换成新的形式,然后用于填充输出字典的值。

以下示例接受一个具有字符串值的字典,返回一个新字典,其中每个值都是源字符串的第一个字符。

NSDictionary* result = [input linq_select:^id(id key, id value) {
    return [value substringToIndex:1];
}];

linq_toArray

- (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"
// )

linq_any

- (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

linq_all

- (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'

linq_count

- (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"

linq_merge

- (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;
// )