前情提要1 : lodash中文学习拾零之Array篇
前情提要2:lodash 中文学习拾零之 Chain篇
3、Collection(集合)与Array(数组)的区别
对于lodash的初学者而言,可能一下子搞不清楚为什么lodash提供了Array和Collection两类方法?表面上看起来好像这两套方法有些地方是可以互换的,那么我们就进一步来澄清什么是Collection方法,什么是Array方法。
在lodash中,Collection是一个抽象的概念,指的是那些我们准备用来迭代的Javascript object,可以是 数组、字符串或者object对象,至于这些数据类型之间的差别细节,则被lodash隐藏起来了,作为开发者你不用操心。
而lodash的 Array方法则没有那么抽象,它们要求你处理的就是实实在在的数组。从这个意义上讲,即便Array方法没有显式的检查你提交的数据类型,但是它们要求你提交的数据得有数值型length属性(a numerical length property)。
实际上,在大部分时候,你并不需要严格得区分 arrays 和 collections 之间的差别,因为你面对的大多数collections都是以数组的形式出现的,只有在比较少的情况下,你会面临着差别 。所以你只要记住,Array方法是严格要求数据类型(有数值型length属性)的就行了。
3.1 遍历collection元素
3.1.1 .forEach
_.forEach是处理Collection最基础的函数了,下面这个例子非常简单,就是告诉你forEach是如何进行迭代(循环)的:
var collection = [
'红',
'黄',
'蓝',
'绿'
];
_.forEach(collection, function(name) {
console.log(name);
});
// →
// 红
// 黄
// 蓝
// 绿
_.forEach有三个参数,第一个参数是要处理的collection,第二个参数是迭代器(以匿名函数的形式出现),第三个参数是 The this binding of iteratee(可以理解为迭代器里的this绑定的方法).
_.forEach文档定义如下:
Aliases(别名):
_.eachArguments(参数):
1、collection (Array|Object|string): The collection to iterate over.
2、[iteratee=_.identity] (Function): The function invoked per iteration.
3、 [thisArg] (*): The this binding of iteratee.
//例子
var collection = [100,200,300,400];
_.forEach(collection, function(value) {
console.log(this.sqrt(value)); //对collection中的每个值开方
},
Math); //本例中thisArg绑定了Math的方法
//10
//14.142135623730951
//17.320508075688775
//20
当迭代发生的时候,每轮到一个元素都激活一次迭代器,迭代器有三个参数:
(1)被迭代的元素,(2)该元素的index或者key,(3)collection。
在迭代器返回false值时,允许整个迭代过程提早结束,如下面的例子:
var myCollection = [
'阿猫',
'阿狗',
'小黑',
'阿花'
];
_.forEach(myCollection, function(name, index, collection) {
if (name === '小黑') {
console.log('小黑的index: ' + index);
console.log(collection[index]);
return false;
}
});
// → 小黑的index: 1
// → 小黑
3.1.2 .forEachRight
与_.forEach的执行方向相反,从最右侧的元素先开始进行迭代:
_([1, 2]).forEachRight(function(n) {
console.log(n);
}).value();
// → 2
// → 1
3.3 排序
排序是我们对collection进行操作时会经常遇到的问题,javascript自带的sort()、reverse()之类的函数,远远不能满足我们的需求。
3.3.1 .sortBy
_.sortBy会根据迭代器对输入的collection的每个数组元素进行计算后排序(升序)并生成一个新的数组,同时不会影响原collection的排序(这一点与原生的sort 不同)。
.sortBy有三个参数,与.forEach相同。
迭代器本身还有三个参数(value, index|key, collection),也与.forEach的迭代器相同。
//_.sortBy例子
_.sortBy([1, 2, 3], function(n) {
return Math.sin(n); //直接在迭代器中使用Math
});
// → [3, 1, 2]
_.sortBy([1, 2, 3], function(n) {
return this.sin(n); //在迭代器内用this绑定了Math
}, Math);
// → [3, 1, 2]
var users = [
{ 'user': 'fred' },
{ 'user': 'pebbles' },
{ 'user': 'barney' }
];
// 使用'user'作为快捷方式制定按'user'进行排序,简化了迭代器
_.pluck(_.sortBy(users, 'user'), 'user');
// → ['barney', 'fred', 'pebbles']
_.sorBy不只是对数组起作用,其实对字符串也一样起作用
_.sortBy('cBa').join('')
//Bac ,如果要返回值还是字符串,需要用join函数
_.sortBy('cBa')
//[ 'B', 'a', 'c' ] ,缺省情况下 sortBy的返回值都是数组
排序之后动态添加数组元素,使用_sortedIndex,可以让新元素自动按拟定好的顺序排列进去,这是多体贴的设计啊!
var collection = [
'Carl',
'Gary',
'Luigi',
'Otto'
];
var name = 'Luke'; //新添加一个元素进数组
collection.splice(_.sortedIndex(collection, name), 0, name);
// _.sortedIndex会保持collection已有的次序
// →
// [
// "Carl",
// "Gary",
// "Luigi",
// "Luke",
// "Otto" // ]
3.3.2 _.sortByAll
与_.sortBy类似,但是可以对多个property name 或者多个迭代器进行排序,排序的顺序按照迭代器的排列顺序
var users = [
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 36 },
{ 'user': 'fred', 'age': 42 },
{ 'user': 'barney', 'age': 34 }
];
//先按'user'排序,再按'age'排序
_.map(_.sortByAll(users, ['user', 'age']), _.values);
// → [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]]
_.map(_.sortByAll(users, 'user', function(chr) {
return Math.floor(chr.age / 10);
}), _.values);
// → [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]
3.3.3 _.sortByOrder
类似于_.sortByAll,但是可以制定每一个迭代器的升降顺序,非常灵活!
var users = [
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 34 },
{ 'user': 'fred', 'age': 42 },
{ 'user': 'barney', 'age': 36 }
];
// 对'user'进行升序排列,对于'age'进行降序排列
_.map(_.sortByOrder(users, ['user', 'age'], ['asc', 'desc']), _.values);
// → [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]
3.4 数据过滤和搜索
在已有数据集合中搜索和过滤数据是经常用到的功能,lodash提供了一系列工具来帮助我们实现数据的过滤和搜索。
3.4.1 _.where
.where是lodash当中最简单的过滤数据的方法,只有每一个条件都符合的数据才会被挑出来。
.where的好处是简单、精准,严格。
Note: This method supports comparing arrays, booleans, Date objects,
numbers, Object objects, regexes, and strings. Objects are compared by
their own, not inherited, enumerable properties. For comparing a
single own or inherited property value see _.matchesProperty.
注意:.where支持 数组, 布尔值, 日期, 数值, Object类型, 正则表达式,字符串的比较,Object类型只能比较其自身非继承的可枚举属性,要想比较单个Object或继承属性需要用 _.matchesProperty
var collection = [
{ name: '路飞', age: 19, gender: '男' },
{ name: '索隆', age: 21, gender: '男' },
{ name: '娜美', age: 20, gender: '女' },
{ name: '罗宾', age: 30, gender: '女' }
];
_.where(collection, { age: 30, gender: '女' });
// →
// [
// { name: '罗宾', age: 30, gender: '女' }
// ]
3.4.2 .filter
_.filter有多种过滤方法
(1) _.matches
(2)_.matchesProperty
(3)_.property
//例子
_.filter([4, 5, 6], function(n) {
return n % 2 == 0;
});
// → [4, 6]
var users = [
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'fred', 'age': 40, 'active': false }
];
// using the `_.matches` callback shorthand
_.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user');
// → ['barney']
// using the `_.matchesProperty` callback shorthand
_.pluck(_.filter(users, 'active', false), 'user');
// → ['fred']
// using the `_.property` callback shorthand
_.pluck(_.filter(users, 'active'), 'user');
// → ['barney']
_.filter还可以接受迭代器进行过滤
var collection = [
{ type: '衬衣', size: 'L' },
{ type: '裤子', size: 'S' },
{ type: 'T恤', size: 'XL' },
{ type: '鞋子', size: 'M' }
];
_.filter(collection, function(item) {
return item.size === 'L' || item.size === 'M';
});
// → // [
// { type: "衬衣", size: "L" },
// { type: "鞋子", size: "M" }
// ]
3.4.3 .reject
当你不知道从集合里面直接选出什么,但是却明确知道不要选什么的时候,你可以用.reject来处理了。
//例子中把 enabled:false的数据筛掉了
var collection = [
{ name: '冰与火之歌', enabled: true },
{ name: '星球大战', enabled: false },
{ name: '指环王', enabled: false },
{ name: '哈里波特', enabled: true }
];
_.reject(collection, { enabled: false });
// →
// [
// { name: "冰与火之歌", enabled: true },
// { name: "哈里波特", enabled: true }
// ]
3.5 只找一条符合要求的数据
有时候你并不需要找到所有符合条件的数据,而是只要第一条就够了,那么就用find系列(find、findLast、findWhere),find系列的参数和filter一样,但是返回值不再是数组,而是一个符合条件的元素。如果没找到符合条件的,那就返回undefined
3.5.1_.find
找出第一条符合条件的数组元素(即从数组的左侧找起)
var users = [
{ 'user': '钱夫人', 'age': 36, 'active': true },
{ 'user': '阿土伯', 'age': 45, 'active': false },
{ 'user': '孙小美', 'age': 20, 'active': true },
{ 'user': '糖糖', 'age': 20, 'active': true }
];
_.result(_.find(users, function(chr) {
return chr.age < 40; //找出第一个age小于40的元素
}), 'user');
// → '钱夫人'
3.5.2_.findLast
找出最后一条符合条件的数组元素(即从数组的右侧找起)
_.findLast([1, 2, 3, 4], function(n) {
return n % 2 == 1;
});
// → 3
3.5.3 _.findWhere
参数source是个object,严格按照source来查找匹配的第一项
var users = [
{ 'user': '钱夫人', 'age': 36, 'active': true },
{ 'user': '阿土伯', 'age': 45, 'active': false }
];
_.result(_.findWhere(users, { 'age': 36, 'active': true }), 'user');
// → '钱夫人'
_.result(_.findWhere(users, { 'age': 45, 'active': false }), 'user');
// → '阿土伯'
3.6 “集合”改造(Transforming collections)
lodash有一批工具用于把Collection(集合)改造为新的数据结构,而且还有工具把两个或更多的集合合并到一个集合里去。这些工具可以把我们程序狗从一些繁重而重复的劳动中解放出来,并且让我们的代码更加简介易懂。
3.6.1_.groupBy
在集合中,往往有些元素的某项属性的值是相同的,那么我们就可以这项属性为key进行分组,从而产生一个新的JSON对象,这个JSON对象的key就是被分组的值。
_.groupBy有三个参数,第一个参数是要处理的collection,第二个参数是迭代器(以匿名函数的形式出现),第三个参数是 The this binding of iteratee(可以理解为迭代器里的this绑定的方法)。
groupBy与indexBy的区别在于:groupBy聚合的值以数组形式出现,indexBy获得的值以Object形式出现。
//比如下面这个例子:以size作为分组的依据,得到的新JSON中有"S"和"M"两个Key
var collection = [
{ name: 'Lori', size: 'S' },
{ name: 'Johnny', size: 'M' },
{ name: 'Theresa', size: 'S' },
{ name: 'Christine', size: 'S' }
];
_.groupBy(collection, 'size');
// {
// S:[
// { name: "Lori", size: "S" },
// { name: "Theresa", size: "S" },
// { name: "Christine", size: "S" }
// ]
// M: [
// { name: "Johnny", size: "M" }
// ]
// }
//也可以用迭代器进行分类,并用迭代器指定key的名称
var collection = [
{ name: '小黄', age: 20 },
{ name: '老李', age: 50 },
{ name: '老朱', age: 67 },
{ name: '大蒋', age: 39 }
];
_.groupBy(collection, function(item) {
return item.age > 65 ? 'retired' : 'working';
});
// →
// {
// [
// working: [
// { name: "小黄", age: 20 },
// { name: "老李", age: 50 },
// { name: "大蒋", age: 39 }
// ],
// retired:[
// { name: "老朱", age: 67 }
// ]
// }
3.6.2 min,max
_.min(collection, [iteratee], [thisArg])
_.max(collection, [iteratee], [thisArg])
找出那个符合条件的最小(或者最大)的数组元素。
var collection = [
{ name: 'Douglas', age: 52, experience: 5 },
{ name: 'Karen', age: 36, experience: 22 },
{ name: 'Mark', age: 28, experience: 6 },
{ name: 'Richard', : age: 30, experience: 16 }
];
//用Key来查找
_.min(collection, 'age'),
// → { name: "Mark", age: 28, experience: 6 }
//用迭代器来查找
_.max(collection, function(item) {
return item.age + item.experience;
});
// → { name: "Karen", age: 36, experience: 22 }
3.7 集合的扁平化和压缩
集合中数据有时是嵌套结构的,我们需要对它们进行扁平化处理;
而有些时候,集合中的元素包含一些没用的值,我们可以对其进行压缩处理。
通过扁平化和压缩,我们可以得到更加精炼的数据结构,以便进行分析和展示。
//以下是个多种工具结合进行处理的例子:
var collection = [
{ employer: 'Lodash', employees: [
{ name: 'Barbara' },
{ name: 'Patrick' },
{ name: 'Eugene' }
]},
{ employer: 'Backbone', employees: [
{ name: 'Patricia' },
{ name: 'Lillian' },
{ name: 'Jeremy' }
]},
{ employer: 'Underscore', employees: [
{ name: 'Timothy' },
{ name: 'Bruce' },
{ name: 'Fred' }
]} ];
//1、用.pluck挑出需要的Key及其value
//注意pluck拔出的数据是个二维数组,所以还要用flatten将二维数组降为一维数组
var employees = _.flatten(_.pluck(collection, 'employees'));
//2、通过迭代器筛选出要使用的数据
_.filter(employees, function(employee) {
return (/^[bp]/i).test(employee.name);
});
// →
// [
// { name: "Barbara" },
// { name: "Patrick" },
// { name: "Patricia" },
// { name: "Bruce" }
// ]
3.8 鉴别部分或全部的集合元素 some、every
3.8.1_.some
some的作用是判断只要集合中有符合要求的属性,就返回true,否则返回false
_.some([null, 0, 'yes', false], Boolean);
// → true
var users = [
{ 'user': 'barney', 'active': true },
{ 'user': 'fred', 'active': false }
];
// using the `_.matches` callback shorthand
_.some(users, { 'user': 'barney', 'active': false });
// → false
// using the `_.matchesProperty` callback shorthand
_.some(users, 'active', false);
// → true
// using the `_.property` callback shorthand
_.some(users, 'active');
// → true
3.8.2_.every
_.every则是判断是否集合中每一个元素都包含
var collection = [
{ name: 'Jonathan' },
{ first: 'Janet' },
{ name: 'Kevin' },
{ name: 'Ruby' }
];
if (!_.every(collection, 'name')) {
return '缺少name属性;
}
// → "缺少name属性"
lodash Collection小结:
1、数组(Array)肯定是集合(Collection),所以所有的Colleciton方法都适用于数组;
2、集合不一定都是数组;
3、本节中还有少量的Collection方法没有提及,如map、reduce、reduceRight将在 Map/Reduce篇中介绍。