lodash 中文学习拾零之 Map/Reduce篇

作者:Soaring_Tiger 转载请注明出处 http://blog.csdn.net/Soaring_Tiger

前情提要1 : lodash中文学习拾零之Array篇
前情提要2:lodash 中文学习拾零之 Chain篇
前情提要3:lodash 中文学习拾零之 Collection篇
前情提要4:lodash 中文学习拾零之 Object篇
前情提要5: lodash 中文学习拾零之 Function篇

6、用lodash进行数据的Map/Reduce

我们在编程过程中,大量遇到的工作是数据转换,前面几篇文章提到了lodash在数据转换中的作用,本篇将围绕Map/Reduce进行更深入的探索。


6.1 挑选数据 .pluck 或者 .map

前面我们已经看到了很多用_.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的功能则更加强大 。


6.2 映射集合 Mapping collections

6.2.1 用.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":"羽"}
//]

6.2.2 执行计算

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
   //   }
   // ]

6.2.3 调用函数

迭代器可以直接使用现成的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" ]

6.2.4 过滤并且映射

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

6.3 映射对象

在lodash的世界中,对象也被看做是集合的一种,但是也有其特殊性,所以我们在此单独讨论对象的映射。


6.3.1 处理对象中的key

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 },
// ...
// ]

6.3.2 调用函数

对象的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 }
// ]

6.3.3 映射key-value对

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"
// ]

6.4 化简集合

本节介绍_.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)

6.4.1 求和(Summing values)

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

6.4.2 过滤和化简(Filtering and reducing)

所谓累加器的作用就是提供了一个初始值

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
   // }

6.4.3 最大值、最小值、平均值操作

//求最大最小值
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"

6.5 化简对象

6.5.1 化简Keys

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

6.5.2 对象累加器

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"

6.5.3 绑定上下文

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" }
   // ]

6.6 Map/Reduce 模式

6.6.1 通用回调函数

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

6.6.2 Map/Reduce 链

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小结:

本篇主要介绍了lodash在 Map/Reduce方面的应用:
Map部分,我们要考虑挑选哪些Key以及如何进行计算,然后进行Map操作,将操作对象映射到新的对象或集合中去。
Reduce部分,介绍如何精简数据。

你可能感兴趣的:(javascript)