接着第一课,我们看下collection函数集中剩下的几个
// Invoke a method (with arguments) on every item in a collection. _.invoke = function(obj, method) { var args = slice.call(arguments, 2); return _.map(obj, function(value) { return (method.call ? method || value : value[method]).apply(value, args); }); }; // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { return _.map(obj, function(value){ return value[key]; }); }; // Return the maximum element or (element-based computation). _.max = function(obj, iterator, context) { if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj); if (!iterator && _.isEmpty(obj)) return -Infinity; var result = {computed : -Infinity}; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; computed >= result.computed && (result = {value : value, computed : computed}); }); return result.value; }; // Return the minimum element (or element-based computation). _.min = function(obj, iterator, context) { if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj); if (!iterator && _.isEmpty(obj)) return Infinity; var result = {computed : Infinity}; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; computed < result.computed && (result = {value : value, computed : computed}); }); return result.value; }; // Shuffle an array. _.shuffle = function(obj) { var shuffled = [], rand; each(obj, function(value, index, list) { if (index == 0) { shuffled[0] = value; } else { rand = Math.floor(Math.random() * (index + 1)); shuffled[index] = shuffled[rand]; shuffled[rand] = value; } }); return shuffled; }; // Sort the object's values by a criterion produced by an iterator. _.sortBy = function(obj, iterator, context) { return _.pluck(_.map(obj, function(value, index, list) { return { value : value, criteria : iterator.call(context, value, index, list) }; }).sort(function(left, right) { var a = left.criteria, b = right.criteria; return a < b ? -1 : a > b ? 1 : 0; }), 'value'); }; // Groups the object's values by a criterion produced by an iterator _.groupBy = function(obj, iterator) { var result = {}; each(obj, function(value, index) { var key = iterator(value, index); (result[key] || (result[key] = [])).push(value); }); return result; }; // Use a comparator function to figure out at what index an object should // be inserted so as to maintain order. Uses binary search. _.sortedIndex = function(array, obj, iterator) { iterator || (iterator = _.identity); var low = 0, high = array.length; while (low < high) { var mid = (low + high) >> 1; iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; } return low; }; // Safely convert anything iterable into a real, live array. _.toArray = function(iterable) { if (!iterable) return []; if (iterable.toArray) return iterable.toArray(); if (_.isArray(iterable)) return slice.call(iterable); if (_.isArguments(iterable)) return slice.call(iterable); return _.values(iterable); }; // Return the number of elements in an object. _.size = function(obj) { return _.toArray(obj).length; };
invoke(list,methodName,[*arguments]):和map差不多,都是对list中的每一项执行methodName这个方法,不一样的是invoke支持传递额外的参数,而且第二个参数可以是一个具体的函数,也可以是一个函数名,该函数可以通过listItem[methodName]访问到,例如:
1 _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
2 => [[1, 5, 7], [1, 2, 3]]
pluck(obj,key):key为obj中每一项的某一个属性名,函数执行后返回一个数组,每一项都是obj中每一项对应key的value。
max(obj,iterator,context):遍历obj的每一项并传入iterator中计算,每次计算都会和上一次计算后的结果比较,并记录计算后的最大值和对应的项,最后返回最大值对应的项,如果只传入一个数组作为参数,则返回该数组的最大值。
min:和max类似。
shuffle(obj):洗牌,这个函数其实挺有意思的,它其实就是对给定的一个数组进行洗牌,返回一个新数组,新数组中的每一项都在原数组中,但是顺序是随机的,内部实现是先声明一个洗牌后的数组shuffled,然后遍历原数组的每一项item,生成一个0-shuffled的length-1的随机数random,然后把shuffled的random索引处的值赋给shuffled[length],最后把item放到shuffled[random],这个洗牌算法大家可以参考下这里http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
sortBy(obj,iterator,context):对obj按照iterator映射后的结果进行排序,内部实现是先对obj进行map操作,每一项都映射为一个对象,这个对象的value属性为obj中每一项的原始值,criteria属性为iterator.call(context,value,index,list)后的返回值,然后对这个数组进行排序,排序的依据是criteria属性值的大小,最后用pluck采摘排序后每一项的value属性值(原始值)
groupBy(obj,iterator):类似sql中的groupBy,就是对计算结果进行分组,内部是声明一个result对象,obj的每一项都传入iterator中计算,计算后的值作为result的一个key,如果多个item计算后的key相同,那么就push进result[key],最后返回result
sortedIndex(array,obj,iterator):计算一个obj在一个排好序的数组中的位置,array就是一个排好序的数组,内部实现是使用二分查找法,如果没有传入自定义的比较函数就用_.identity(其实就是比较obj本身)
toArray(iterator):把任何可迭代的对象都转换为数组,支持数组,类数组集合,有原生toArray方法的对象,以及普通对象(返回keys)
size(obj):返回obj toArray后的大小。
这就是Underscore提供给我们的20个collection操作函数,下面我们看下操作array的工具函数:
// Array Functions // --------------- // Get the first element of an array. Passing **n** will return the first N // values in the array. Aliased as `head`. The **guard** check allows it to work // with `_.map`. _.first = _.head = function(array, n, guard) { return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; }; // Returns everything but the last entry of the array. Especcialy useful on // the arguments object. Passing **n** will return all the values in // the array, excluding the last N. The **guard** check allows it to work with // `_.map`. _.initial = function(array, n, guard) { return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); }; // Get the last element of an array. Passing **n** will return the last N // values in the array. The **guard** check allows it to work with `_.map`. _.last = function(array, n, guard) { return (n != null) && !guard ? slice.call(array, array.length - n) : array[array.length - 1]; }; // Returns everything but the first entry of the array. Aliased as `tail`. // Especially useful on the arguments object. Passing an **index** will return // the rest of the values in the array from that index onward. The **guard** // check allows it to work with `_.map`. _.rest = _.tail = function(array, index, guard) { return slice.call(array, (index == null) || guard ? 1 : index); }; // Trim out all falsy values from an array. _.compact = function(array) { return _.filter(array, function(value){ return !!value; }); }; // Return a completely flattened version of an array. _.flatten = function(array, shallow) { return _.reduce(array, function(memo, value) { if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value)); memo[memo.length] = value; return memo; }, []); }; // Return a version of the array that does not contain the specified value(s). _.without = function(array) { return _.difference(array, slice.call(arguments, 1)); }; // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. _.uniq = _.unique = function(array, isSorted, iterator) { var initial = iterator ? _.map(array, iterator) : array; var result = []; _.reduce(initial, function(memo, el, i) { if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) { memo[memo.length] = el; result[result.length] = array[i]; } return memo; }, []); return result; }; // Produce an array that contains the union: each distinct element from all of // the passed-in arrays. _.union = function() { return _.uniq(_.flatten(arguments, true)); }; // Produce an array that contains every item shared between all the // passed-in arrays. (Aliased as "intersect" for back-compat.) _.intersection = _.intersect = function(array) { var rest = slice.call(arguments, 1); return _.filter(_.uniq(array), function(item) { return _.every(rest, function(other) { return _.indexOf(other, item) >= 0; }); }); }; // Take the difference between one array and another. // Only the elements present in just the first array will remain. _.difference = function(array, other) { return _.filter(array, function(value){ return !_.include(other, value); }); }; // Zip together multiple lists into a single array -- elements that share // an index go together. _.zip = function() { var args = slice.call(arguments); var length = _.max(_.pluck(args, 'length')); var results = new Array(length); for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i); return results; }; // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), // we need this function. Return the position of the first occurrence of an // item in an array, or -1 if the item is not included in the array. // Delegates to **ECMAScript 5**'s native `indexOf` if available. // If the array is large and already in sort order, pass `true` // for **isSorted** to use binary search. _.indexOf = function(array, item, isSorted) { if (array == null) return -1; var i, l; if (isSorted) { i = _.sortedIndex(array, item); return array[i] === item ? i : -1; } if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item); for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i; return -1; }; // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. _.lastIndexOf = function(array, item) { if (array == null) return -1; if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item); var i = array.length; while (i--) if (array[i] === item) return i; return -1; }; // Generate an integer Array containing an arithmetic progression. A port of // the native Python `range()` function. See // [the Python documentation](http://docs.python.org/library/functions.html#range). _.range = function(start, stop, step) { if (arguments.length <= 1) { stop = start || 0; start = 0; } step = arguments[2] || 1; var len = Math.max(Math.ceil((stop - start) / step), 0); var idx = 0; var range = new Array(len); while(idx < len) { range[idx++] = start; start += step; } return range; };
first|head(array,n,guard):如果只传array则返回array[0],如果传n了就返回前n个元素,第三个参数guard文档上说是为了允许它和map一起工作,但是我还是不太理解。
initial(array,n,guard):返回数组中不包括后n个元素的所有元素,如果没传n则n=1
last(array,n,guard):返回数组的后n(没传nl时取1)个元素
rest|tail(array,index,guard):返回数组中除了第一个元素外的其余的元素,如果传入index则返回array[index]及以后的元素
compact(array):返回一个新数组,新数组是过滤掉数组中所有的falsy value,在js中即为undefined,null,0,false,NaN,"",内部是使用!!value来转换为布尔值。
flatten(array,shallow):返回一个新数组,如果shallow为true,则把array中的array都拆分为单个的值(递归调用flatten)。
difference(array,other):返回一个新数组,该数组是array和other的差集
without(array,...):返回一个新数组,该数组是array和arguments.slice(1)的差集
uniq|unique(array,isSorted,iterator):返回一个新数组,该数组是原数组去掉重复元素的版本,如果array是已经排好序的,那么内部会调用_,lash(array)来检测是否重复,这样会很快,如果不是排好序的内部会调用_.include(array,el)这样又是一层循环,相对前者要慢些。
union():unique的包装函数,它把arguments用flatten(array,true)转换为数组,再对其调用unique返回新的无重复的数组。
intersection|intersect(array):函数的参数是传入2个或更多的数组,运行结果是返回这几个数组的交集
zip(*arrays):参数是多个数组,运行结果是返回一个数组,数组的长度为多个数组中length最大的那个,数组的第i项为各个数组相应的第i个元素组成的新数组
indexOf(array,item,isSorted):返回item在array中的索引,如果是排好序的array则内部是调用_.sortedIndex进行二分查找,如果没有排序则调用es5的indexOf,如果不支持则逐个遍历吧,找不到的话就返回-1咯。
lashIndexOf(array,item):和indexOf差不多,你懂的,只是不支持排好序的情况
range([start],stop,[step]):和python的range实现类似的,如果只传一个参数则为stop