作者:Soaring_Tiger 转载请注明出处 http://blog.csdn.net/Soaring_Tiger
前情提要1 : lodash中文学习拾零之Array篇
前情提要2:lodash 中文学习拾零之 Chain篇
前情提要3:lodash 中文学习拾零之 Collection篇
前情提要4:lodash 中文学习拾零之 Object篇
前情提要5: lodash 中文学习拾零之 Function篇
我们在编程过程中,大量遇到的工作是数据转换,前面几篇文章提到了lodash在数据转换中的作用,本篇将围绕Map/Reduce进行更深入的探索。
前面我们已经看到了很多用_.pluck来挑选数据的例子
var collection = [
{ name: 'Virginia', age: 45 },
{ name: 'Debra', age: 34 },
{ name: 'Jerry', age: 55 },
{ name: 'Earl', age: 29 }
];
_.pluck(collection, 'age');
// → [ 45, 34, 55, 29 ]
其实我们也可以用map来做同样的事情
var collection = [
{ name: 'Michele', age: 58 },
{ name: 'Lynda', age: 23 },
{ name: 'William', age: 35 },
{ name: 'Thomas', age: 41 }
];
_.map(collection, 'name');
// →
// [
// "Michele",
// "Lynda",
// "William",
// "Thomas"
// ]
看上去结果是一样的,其实.pluck就是在.map的基础上进行了封装,而且pluck只能以字符串(String)作为挑选的参数,而map的功能则更加强大 。
别名:_.collect
.map(collection, [iteratee=_.identity], [thisArg])
第一个参数是要处理的数组,
第二个参数是迭代器,该迭代器可以是函数,可以是对象,也可以是字符串
var collection = [
{ first: '赵', last: '子龙', age: 23 },
{ first: '马', last: '超', age: 31 },
{ first: '张', last: '飞', age: 44 },
{ first: '关', last: '羽', age: 38 }
];
//用函数做迭代器
_.map(collection, function(item) {
return _.pick(item, [ 'first', 'last' ]);
});
// →
// [
// { first: '赵', last: '子龙'},
// { first: '马', last: '超'},
// { first: '张', last: '飞' },
// { first: '关', last: '羽' }
// ]
//用对象做迭代器
_.map(collection,{first: '赵'});
//[true,false,false,false]
//用omit去掉指定的属性
_.map(collection, function(item) {
return _.omit(item, 'first');
});
//[
// {"last":"子龙","age":23},
// {"last":"超","age":31},
// {"last":"飞","age":44},
// {"last":"羽","age":38}
//]
//用迭代器设置筛选条件
function invalidAge(value, key) {
return key === 'age' && value < 40;
}
_.map(collection, function(item) {
return _.omit(item, invalidAge);
});
//[
// {"first":"赵","last":"子龙"},
// {"first":"马","last":"超"},
// {"first":"张","last":"飞","age":44},
// {"first":"关","last":"羽"}
//]
var collection = [
{ name: 'Valerie', jqueryYears: 4, cssYears: 3 },
{ name: 'Alonzo', jqueryYears: 1, cssYears: 5 },
{ name: 'Claire', jqueryYears: 3, cssYears: 1 },
{ name: 'Duane', jqueryYears: 2, cssYears: 0 }
];
//在迭代器中,给每个数组元素添加了两个经过计算得来的属性
_.map(collection, function(item) {
return _.extend({
experience: item.jqueryYears + item.cssYears,
specialty: item.jqueryYears >= item.cssYears ?
'jQuery' : 'CSS'},
item);
});
// →
// [
// {
// experience": 7,
// specialty": "jQuery",
// name": "Valerie",
// jqueryYears": 4,
// cssYears: 3
// },
// {
// experience: 6,
// specialty: "CSS",
// name: "Alonzo",
// jqueryYears: 1,
// cssYears: 5
// },
// {
// experience: 4,
// specialty: "jQuery",
// name: "Claire",
// jqueryYears: 3,
// cssYears: 1
// },
// {
// experience: 2,
// specialty: "jQuery",
// name: "Duane",
// jqueryYears: 2,
// cssYears: 0
// }
// ]
迭代器可以直接使用现成的lodash函数,保证能用的有以下这些:
ary, callback, chunk, clone, create, curry, curryRight, drop, dropRight, every, fill, flatten, invert, max, min, parseInt, slice, sortBy, take, takeRight, template, trim, trimLeft, trimRight, trunc, random, range, sample, some, sum, uniq, words
事实上,只要是只有一个输入参数,并且有一个新的基于输入参数的返回值的函数都可以尝试用来作为map()的迭代器。
//获取指定数组的每个元素的size
var collection = [
[ 1, 2 ],
[ 1, 2, 3 ],
{ first: 1, second: 2 },
{ first: 1, second: 2, third: 3 }
];
_.map(collection, _.size);
// → [ 2, 3, 2, 3 ]
//迭代器也可以多次迭代
//下面的代码用compose构建了一个新的数组
//新的数组是把原数组的每一个元素重排之后,再挑出来的第一个数组元素组成的
var collection = [
[ 'Evan', 'Veronica', 'Dana' ],
[ 'Lila', 'Ronald', 'Dwayne' ],
[ 'Ivan', 'Alfred', 'Doug' ],
[ 'Penny', 'Lynne', 'Andy' ]
];
_.map(collection, _.compose(_.first, function(item) {
return _.sortBy(item);
}));
// → [ "Dana", "Dwayne", "Alfred", "Andy" ]
var collection = [
{ name: 'Karl', enabled: true },
{ name: 'Sophie', enabled: true },
{ name: 'Jerald', enabled: false },
{ name: 'Angie', enabled: false }
];
//用compose构成了一个新的函数,新的函数以collection作为输入参数
_.compose(
_.partialRight(_.map, 'name'),
_.partialRight(_.filter, 'enabled')
)(collection);
// → [ "Karl", "Sophie" ]
在lodash的世界中,对象也被看做是集合的一种,但是也有其特殊性,所以我们在此单独讨论对象的映射。
var object = {
first: 'Ronald',
last: 'Walters',
employer: 'Packt'
};
_.map(_.sortBy(_.keys(object)), function(item) {
return object[item];
});
// → [ "Packt", "Ronald", "Walters" ]
var users = {},
preferences = {};
_.each(_.range(100), function() {
var id = _.uniqueId('user-');
users[id] = { type: 'user' };
preferences[id] = { emailme: !!(_.random()) };
});
_.map(users, function(value, key) {
return _.extend({ id: key }, preferences[key]);
});
// →
// [
// { id: "user-1", emailme: true },
// { id: "user-2", emailme: false },
// ...
// ]
对象的key对应的不仅仅是数字和字符串,也可以是函数,但是随着上下文的改变,函数并不是一直可以正常使用的
var object = {
first: 'Roxanne',
last: 'Elliot',
name: function() {
return this.first + ' ' + this.last;
},
age: 38,
retirement: 65,
working: function() {
return this.retirement - this.age;
}
};
_.map(object, function(value, key) {
var item = {};
item[key] = _.isFunction(value) ? object[key]() : value
 return item;
});
// →
// [
// { first: "Roxanne" },
// { last: "Elliot" },
// { name: "Roxanne Elliot" },
// { age: 38 },
// { retirement: 65 },
// { working: 27 }
// ]
//调用result()也可以得到相同结果,并且更加简洁
_.map(object, function(value, key) {
var item = {};
item[key] = _.result(object, key);
return item;
});
// →
// [
// { first: "Roxanne" },
// { last: "Elliot" },
// { name: "Roxanne Elliot" },
// { age: 38 },
// { retirement: 65 },
// { working: 27 }
// ]
function capitalize(s) {
return s.charAt(0).toUpperCase() + s.slice(1);
}
function format(label, value) {
return ' + capitalize(label) + ':' +
'' + value + '';
}
var object = {
first: 'Julian',
last: 'Ramos',
age: 43
};
_.map(_.pairs(object), function(pair) {
return format.apply(undefined, pair);
});
// →
// [
// "Julian",
// "Ramos",
// "43"
// ]
本节介绍_.reduce()的用法
.reduce(collection, [iteratee=_.identity], [accumulator], [thisArg])
第一个参数是要处理的集合,可以是数组、对象、字符串
第二个参数是迭代器
第三个参数是累加器(的初始值)
第四个参数是thisArg
_.reduce([1, 2], function(total, n) {
return total + n;
});
// → 3
_.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) {
result[key] = n * 3;
return result;
}, {});
// → { 'a': 3, 'b': 6 } (iteration order is not guaranteed)
var collection = [
{ ram: 1024, storage: 2048 },
{ ram: 2048, storage: 4096 },
{ ram: 1024, storage: 2048 },
{ ram: 2048, storage: 4096 }
];
_.reduce(collection, function(result, item) {
return result + item.ram;
}, 0);
// → 6144
_.reduce(collection, function(result, item) {
return result + item.storage;
}, 0);
// → 12288
所谓累加器的作用就是提供了一个初始值
var collection = [
{ name: 'Gina', age: 34, enabled: true },
{ name: 'Trevor', age: 45, enabled: false },
{ name: 'Judy', age: 71, enabled: true },
{ name: 'Preston', age: 19, enabled: false }
];
_.reduce(_.filter(collection, 'enabled'), function(result, item) {
result.names.push(item.name);
result.years += item.age;
return result;
}, { names: [], years: 0 });
// →
// {
// names: ["Gina","Judy" ],
// years: 105
// }
//求最大最小值
function score(item) {
return _.reduce(item.scores, function(result, score) {
return result + score;
});
}
var collection = [
{ name: 'Madeline', scores: [ 88, 45, 83 ] },
{ name: 'Susan', scores: [ 79, 82, 78 ] },
{ name: 'Hugo', scores: [ 90, 84, 85 ] },
{ name: 'Thomas', scores: [ 74, 69, 78 ] }
];
_.min(collection, score);
// →
// {
// name: "Madeline",
// scores: [
// 88,
// 45,
// 83
// ]
// }
_.max(collection, score);
// →
// {
// name: "Hugo",
// scores: [
// 90,
// 84,
// 85
// ]
// }
//求平均值
function average(items) {
return _.reduce(items, function(result, item) {
return result + item;
}) / items.length;
}
var collection = [
{ name: 'Anthony', scores: [ 89, 59, 78 ] },
{ name: 'Wendy', scores: [ 84, 80, 81 ] },
{ name: 'Marie', scores: [ 58, 67, 63 ] },
{ name: 'Joshua', scores: [ 76, 68, 74 ] }
];
_.reduce(collection, function(result, item, index, coll) {
var ave = average(item.scores);
result.push(ave);
if (index === (coll.length - 1)) {
return average(result);
}
return result;
}, []).toFixed(2);
// → "73.08"
var object = {
first: 'Kerry',
last: 'Singleton',
age: 41
},
allowed = [ 'first', 'last' ];
_.reduce(object, function(result, value, key) {
if (_.contains(allowed, key)) {
result[key] = value;
}
return result;
}, {});
// → { first: "Kerry", last: "Singleton" }
_.pick(object, allowed);
// → { first: "Kerry", last: "Singleton" }
var object = {
first: '<strong>Nicole</strong>',
last: '<strong>Russel</strong>',
age: 26
};
_.transform(object, function(result, value, key) {
if (_.isString(value)) {
result[key] = _.unescape(value);
}
});
// →
// {
// first: "Nicole",
// last: "Russel"
// }
function Person(first, last) {
this.first = first;
this.last = last;
}
Person.prototype.name = function name() {
return this.first + ' ' + this.last;
};
var object = new Person('Alex', 'Rivera');
_.transform(object, function(result, value, key) {
if (_.isString(value)) {
result[key] = value.toUpperCase();
}
}).name();
// → "ALEX RIVERA"
var app = {
states: [
'running',
'off',
'paused'
],
machines: [
{ id: _.uniqueId(), state: 1 },
{ id: _.uniqueId(), state: 0 },
{ id: _.uniqueId(), state: 0 },
{ id: _.uniqueId(), state: 2 }
]
};
var mapStates = _.partialRight(_.map, function(item) {
return _.extend({
state: this.states[item.state]
}, _.pick(item, 'id'));
}, app);
mapStates(app.machines);
// →
// [
// { state: "off", id: "1" },
// { state: "running", id: "2" },
// { state: "running", id: "3" },
// { state: "paused", id: "4" }
// ]
function add(item) {
var result = _.clone(item);
result[this.prop] += this.value;
return result;
}
function upper(item) {
var result = _.clone(item);
result[this.prop] = result[this.prop].toUpperCase();
return result;
}
var collection = [
{ name: 'Gerard', balance: 100 },
{ name: 'Jean', balance: 150 },
{ name: 'Suzanne', balance: 200 },
{ name: 'Darrell', balance: 250 }
];
var mapAdd = _.partial(_.map, collection, add),
mapUpper = _.partial(_.map, collection, upper);
mapAdd({ prop: 'balance', value: 50 });
// →
// [
// { name: "Gerard", balance: 150 },
// { name: "Jean", balance: 200 },
// { name: "Suzanne", balance: 250 },
// { name: "Darrell", balance: 300 }
// ]
mapAdd({ prop: 'balance', value: 100 });
// →
// [
// { name: "Gerard", balance: 200 },
// { name: "Jean", balance: 250 },
// { name: "Suzanne", balance: 300 },
// { name: "Darrell", balance: 350 }
// ]
mapUpper({ prop: 'name'});
// →
// [
// { name: "GERARD", balance: 100 },
// { name: "JEAN", balance: 150 },
// { name: "SUZANNE", balance: 200 },
// { name: "DARRELL", balance: 250 }
// ]
function sum(a, b) {
return a + b[this.prop];
}
var collection = [
{ low: 40, high: 70 },
{ low: 43, high: 83 },
{ low: 39, high: 79 },
{ low: 45, high: 74 }
];
var reduceSum = _.partial(_.reduce, collection, sum, 0);
reduceSum({ prop: 'low' });
// → 167
reduceSum({ prop: 'high' });
// → 306
var collection = [
{ name: 'Wade', balance: 100 },
{ name: 'Donna', balance: 125 },
{ name: 'Glenn', balance: 90 },
{ name: 'Floyd', balance: 110 }
],
bonus = 25;
var mapped = _.map(collection, function(item) {
return _.extend({
bonus: item.balance + bonus
}, item);
});
_.reduce(mapped, function(result, item, index, coll) {
result += (item.bonus - item.balance) / item.bonus;
if (index === (coll.length - 1)) {
result = result / coll.length * 100;
}
 return result;
}, 0).toFixed(2) + '%';
// → "19.23%"
本篇主要介绍了lodash在 Map/Reduce方面的应用:
Map部分,我们要考虑挑选哪些Key以及如何进行计算,然后进行Map操作,将操作对象映射到新的对象或集合中去。
Reduce部分,介绍如何精简数据。