继续从其他语言吸收精华,这次是Linq from C#。

Introduction to Linq

Linq was added to the C# language back in 2007. The name linq stands for Language Integrated Query, and as the name implies, its original aim was to more tightly integrate queries into the C# language.

As an example, when querying database developers find themselves writing queries using SQL statements embedded within literal strings:

string query = "SELECT * FROM Person WHERE age < 10"

The problem with the above code is that the SQL statements are not checked by the compiler, and as a result, errors will only surface at runtime. This is not a problem that is peculiar to C# - you will find SQL statements embedded within strings in virtually every language.

With Linq queries are no longer literal strings, they are instead constructed using keywords that are part of the C# language itself:

var query =  from p in PersonCollection
               where p.age < 10
               select p;

This allows the compiler to check your query syntax, resulting in less error prone code.

Whilst the application of Linq for querying database is obvious, Linq can be used to query practically anything.

Linq to Objective-C

Colin Eberhardt为Linq做了Objective-C的实现,扩展了NSArrayNSDictionary,为其添加方法可以实现Linq式的查询。

例举几个常用的方法:

linq_where

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

Filters an array of objects based on a predicate that returns true for any object that should be included in the output array.

The following example uses the where method to find people who are 25:

interface Person : NSObject

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

@end

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

linq_select

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

Projects each element of a sequence into a new form. Each element in the array is transformed by a ‘selector’ into a new form, which is then used to populate the output array.

The following example uses a selector that returns the name of each Person instance. The output will be an array of NSString instances.

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

linq_sort

- (NSArray*) linq_sort;
- (NSArray*) linq_sort:(LINQSelector)keySelector;
- (NSArray*) linq_sortDescending;
- (NSArray*) linq_sortDescending:(LINQSelector)keySelector;

Sorts the elements of an array, either via their ‘natural’ sort order, or via a keySelector.

As an example of ‘natural’ sort, the following sorts a collection of NSNumber instances:

NSArray* input = @[@21, @34, @25];
NSArray* sortedInput = [input linq_sort]; // 21, 25, 34

In order to sort an array of Person instances, you can use the key selector:

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

The accompanying ‘descending’ methods simply reverse the sort order:

NSArray* input = @[@21, @34, @25];
NSArray* sortedInput = [input linq_sort]; // 21, 25, 34
NSArray* sortedInput = [input linq_sortDescending]; // 34, 25, 21

linq_ofType

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

Filters the elements of an an array based on a specified type.

In the following example a mixed array of NSString and NSNumber instances is filtered to return just the NSString instances:

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

linq_distinct

- (NSArray*) linq_distinct;
- (NSArray*) linq_distinct:(LINQSelector)keySelector;

Returns distinct elements from a sequence. This simply takes an array of items, returning an array of the distinct (i.e. unique) values in source order.

The no-arg version of this method uses the default method of comparing the given objects. The version that takes a key-selector allows you to specify the value to use for equality for each item.

Here’s an example that returns the distinct values from an array of strings:

NSArray* names = @[@"bill", @"bob", @"bob", @"brian", @"bob"];
NSArray* distinctNames = [names linq_distinct];
// returns bill, bob and brian

Here’s a more complex example that uses the key selector to find people instances with distinct ages:

NSArray* peopleWithUniqueAges = [input linq_distinct:^id(id person) {
    return [person age];
}];

linq_selectMany

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

Projects each element of a sequence to an NSArray and flattens the resulting sequences into one sequence.

This is an interesting one! This is similar to the select method, however the selector must return an NSArray, with the select-many operation flattening the returned arrays into a single sequence.

Here’s a quick example:

NSArray* data = @[@"foo, bar", @"fubar"];

NSArray* components = [data linq_selectMany:^id(id string) {
    return [string componentsSeparatedByString:@", "];  // foo, bar, fubar
}];

linq_aggregate

- (id) linq_aggregate:(LINQAccumulator)accumulator;

Applies an accumulator function over a sequence. This method transforms an array into a single value by applying an accumulator function to each successive element.

Here’s an example that creates a comma separated list from an array of strings:

NSArray* names = @[@"bill", @"bob", @"brian"];

id aggregate = [names linq_aggregate:^id(id item, id aggregate) {
    return [NSString stringWithFormat:@"%@, %@", aggregate, item];
}];
// returns @"bill, bob, brian"

Here’s another example that returns the largest value from an array of numbers:

NSArray* numbers = @[@22, @45, @33];

id biggestNumber = [numbers linq_aggregate:^id(id item, id aggregate) {
    return [item compare:aggregate] == NSOrderedDescending ? item : aggregate;
}];
// returns 45

Reference

LinqToObjectiveC

SL Blog