实际上,一门语言是否优雅归结起来就是其怎么样能更好的避免循环。
引子
给出列表中所有员工的平均薪酬
double totalSalary = 0.0;
for (Employee *employee in employees) {
totalSalary += [employee.salary doubleValue];
}
double averageSalary = totalSalary / [employees count];
示例的做法正确且常见,唯一的问题是不简洁,不优雅。
键值编码的方式是
[employees valueForKeyPath:@"@avg.salary"];
关于集合运算符
OC允许在valueForKeyPath:方法中使用 key path 符号在一个集合中执行方法。
只要在 key path 中看见了@,它都代表了一个特定的集合方法,其结果可以被返回或者链接,就像其他的 key path 一样。
简单的集合运算符
返回的是 strings, number, 或者 dates
//键-值 编码会在必要的时候把基本数据类型的数据自动装箱和拆箱到
//`NSNumber`或者`NSValue`中来确保一切工作正常。
@interface Product : NSObject
@property NSString *name;
@property double price;
@property NSDate *launchedOn;
@end
@implementation Product
+(instancetype)createWithName:(NSString*)name
price:(double)price
launcheON:(NSDate*)launchedOn{
Product *product = [[Product alloc]init];
product.name = name;
product.price = price;
product.launchedOn = launchedOn;
return product;
}
@end
示例数组
[Product createWithName:@"iPhone 5"
price:199
launcheON:[NSDate dateWithTimeIntervalSince1970:100]];
[Product createWithName:@"iPad Mini"
price:329
launcheON:[NSDate dateWithTimeIntervalSince1970:101]];
[Product createWithName:@"MacBook Pro"
price:1699
launcheON:[NSDate dateWithTimeIntervalSince1970:102]];
[Product createWithName:@"iMac"
price:1299
launcheON:[NSDate dateWithTimeIntervalSince1970:103]];
-
@count: 返回一个值为集合中对象总数的NSNumber对象。 -
@sum: 首先把集合中的每个对象都转换为double类型,然后计算其总,最后返回一个值为这个总和的NSNumber对象。 -
@avg: 把集合中的每个对象都转换为double类型,返回一个值为平均值的NSNumber对象。 -
@max: 使用compare:方法来确定最大值。所以为了让其正常工作,集合中所有的对象都必须支持和另一个对象的比较。 -
@min: 和@max一样,但是返回的是集合中的最小值。
[products valueForKeyPath:@"@count"]; // 4
[products valueForKeyPath:@"@sum.price"]; // 3526.00
[products valueForKeyPath:@"@avg.price"]; // 881.50
[products valueForKeyPath:@"@max.price"]; // 1699.00
[products valueForKeyPath:@"@min.launchedOn"]; // Thu Jan 1 08:01:40 1970
也可以简单的通过把 self 作为操作符后面的 key path 来获取一个由
NSNumber组成的数组或者集合的总值。
NSArray *arr = @[@1, @2, @3];
[arr valueForKeyPath:@"@count.self"]
[arr valueForKeyPath:@"@sum.self"]
[arr valueForKeyPath:@"@avg.self"]
[arr valueForKeyPath:@"@max.self"]
[arr valueForKeyPath:@"@min.self"]
对象操作符
返回的是一个数组
示例数组
[Product createWithName:@"iPhone 5"
price:199
launcheON:[NSDate dateWithTimeIntervalSince1970:100]];
[Product createWithName:@"iPhone 5"
price:199
launcheON:[NSDate dateWithTimeIntervalSince1970:100]];
[Product createWithName:@"iPad Mini"
price:329
launcheON:[NSDate dateWithTimeIntervalSince1970:101]];
[Product createWithName:@"MacBook Pro"
price:1699
launcheON:[NSDate dateWithTimeIntervalSince1970:102]];
[Product createWithName:@"iMac"
price:1299
launcheON:[NSDate dateWithTimeIntervalSince1970:103]];
[Product createWithName:@"iMac"
price:1299
launcheON:[NSDate dateWithTimeIntervalSince1970:103]];
@unionOfObjects / @distinctUnionOfObjects: 返回一个由操作符右边的 key path 所指定的对象属性组成的数组。其中@distinctUnionOfObjects 会对数组去重, 而 unionOfObjects 不会。
[products valueForKeyPath:@"@unionOfObjects.name"];
// "iPhone 5" "iPhone 5" "iPad Mini" "MacBook Pro" "iMac" "iMac"
[products valueForKeyPath:@"@distinctUnionOfObjects.name"];
// "iMac" "iPad Mini" "MacBook Pro" "iPhone 5"
数组和集合运算符
数组和集合操作符跟对象操作符很相似,只不过它是在NSArray和NSSet所组成的集合中工作的。
-
@distinctUnionOfArrays/@unionOfArrays: 返回了一个数组,其中包含这个集合中每个数组对于这个操作符右面指定的 key path 进行操作之后的值。正如你期望的,distinct版本会移除重复的值。 -
@distinctUnionOfSets: 和@distinctUnionOfArrays差不多, 但是它期望的是一个包含着NSSet对象的NSSet,并且会返回一个NSSet对象。因为集合不能包含重复的值,所以它只有distinct操作。
NSArray *products1 = @[
[Product createWithName:@"iPhone 5"
price:199
launcheON:[NSDate dateWithTimeIntervalSince1970:100]],
[Product createWithName:@"iPhone 5"
price:199
launcheON:[NSDate dateWithTimeIntervalSince1970:100]],
[Product createWithName:@"iPad Mini"
price:329
launcheON:[NSDate dateWithTimeIntervalSince1970:101]],
[Product createWithName:@"MacBook Pro"
price:1699
launcheON:[NSDate dateWithTimeIntervalSince1970:102]],
[Product createWithName:@"iMac"
price:1299
launcheON:[NSDate dateWithTimeIntervalSince1970:103]],
[Product createWithName:@"iMac"
price:1299
launcheON:[NSDate dateWithTimeIntervalSince1970:103]],
];
NSArray *products2 = @[
[Product createWithName:@"iPhone 13"
price:199
launcheON:[NSDate dateWithTimeIntervalSince1970:100]]
];
[@[products1,products2] valueForKeyPath:@"@distinctUnionOfArrays.name"]
//"iPad Mini" "iMac" "iPhone 13" "MacBook Pro" "iPhone 5"