前言
今天是 lodash-14
,今天带来的是 keyBy
和 orderBy
Api
_.keyBy(collection, [iteratee=_.identity])
Creates an object composed of keys generated from the results of running each element of collection
thru iteratee
. The corresponding value of each key is the last element responsible for generating the key. The iteratee is invoked with one argument: (value) .
Arguments
collection
(Array|Object) : The collection to iterate over.[iteratee=_.identity]
(Function) : The iteratee to transform keys.
Returns
(Object) : Returns the composed aggregate object.
Example
var array = [
{ 'dir': 'left', 'code': 97 },
{ 'dir': 'right', 'code': 100 }
];
_.keyBy(array, function(o) {
return String.fromCharCode(o.code);
});
// => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
_.keyBy(array, 'dir');
// => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
从例子中可以看出来,类似于 groupBy
的用法,对 数组进行分类,如果传入的是函数,则以函数的返回值分类,如果是字符串,则以字符串为key 值进行分类
source
var keyBy = createAggregator(function(result, value, key) {
result[key] = value
});
首先是创建一个 「累加器」 ,传入一个函数作为参数,那么我们可以得知,createAggregator
一定要返回一个函数,返回的函数作为 keyBy 的值
createAggregator
function createAggregator(setter) {
// keyBy 接受的参数
return function(collection, iteratee) {
var func = arrayAggregator,
accumulator = {};
// 格式化 iteratee
let baseIteratee = null
if(typeof iteratee == "function"){
baseIteratee = iteratee
}else if(typeof iteratee == "string"){
baseIteratee = (obj)=>{
return obj[iteratee]
}
}
// setter 是 createAggregator 的传入函数
return func(collection, setter, baseIteratee, accumulator);
};
}
在 createAggregator
内部进行一系列的对参数格式,然后传入 arrayAggregator
现在的重点在 arrayAggregator
function arrayAggregator(array, setter, iteratee, accumulator) {
var index = -1,
length = array == null ? 0 : array.length;
while (++index < length) {
var value = array[index];
// 也就是 createAggregator 内部传入的方法
// value 就是 数组对象中每一条对象
// iteratee(value) 是格式化这条对象获取的值
// 如果是个函数,则不会改变
// 如果是一个字符串,会变成(value)=>value["字符串"]
setter(accumulator, value, iteratee(value), array);
}
return accumulator;
}
`.orderBy(collection, [iteratees=[.identity]], [orders])“
This method is like _.sortBy
except that it allows specifying the sort orders of the iteratees to sort by. If orders
is unspecified, all values are sorted in ascending order. Otherwise, specify an order of “desc” for descending or “asc” for ascending sort order of corresponding values.
Arguments
collection
(Array|Object) : The collection to iterate over.[iteratees=[_.identity]]
(Array[]|Function[]|Object[]|string[]) : The iteratees to sort by.[orders]
(string[]) : The sort orders ofiteratees
.
Returns
(Array) : Returns the new sorted array.
Example
var users = [
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 34 },
{ 'user': 'fred', 'age': 40 },
{ 'user': 'barney', 'age': 36 }
];
// Sort by `user` in ascending order and by `age` in descending order.
_.orderBy(users, ['user', 'age'], ['asc', 'desc']);
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
对 users 中的 user 字段和 age 字段进行排序,先 以 user 为升序,排完序之后,如果 user 有相同的, 以 age 为降序排列
source
这个源码是有一点复杂的,牵扯的文件比较多,我简化了很多
function orderBy2(collection, iteratees, orders) {
var index = -1;
// 进行了缓存处理
// obj=> obj["user"]
// obj=> obj["age"]
iteratees = iteratees.map(path => obj => obj[path]);
// 最终 result 格式化数据结果
//
// [
// {
// criteria: [ 'fred', 48 ],
// index: 0,
// value: { user: 'fred', age: 48 }
// }
// ]
//
//
var result = collection.map(function (value, key, collection) {
var criteria = iteratees.map(function (iteratee) {
return iteratee(value);
});
return { criteria: criteria, index: ++index, value: value };
});
var length = result.length;
// 使用 sort 排序,不过使用了自定义的排序规则
result.sort(function (a, b) {
let r = compareMultiple(a, b, orders);
return r;
});
while (length--) {
result[length] = result[length].value;
}
return result;
}
compareMultiple
多重比较
function compareMultiple(object, other, orders) {
var index = -1,
objCriteria = object.criteria,
othCriteria = other.criteria,
// 要比较的数组长度
length = objCriteria.length,
// 比较器的长度
ordersLength = orders.length;
while (++index < length) {
let x = objCriteria[index];
let x1 = othCriteria[index];
var result = x == x1 ? 0 : x < x1 ? -1 : 1;
if (result) {
if (index >= ordersLength) {
return result;
}
var order = orders[index];
return result * (order == "desc" ? -1 : 1);
}
}
}
其实还是比较好理解的
首先我们看输入参数
以官方例子举例
var users = [
{ user: 'barney', age: 36 },
{ user: 'barney', age: 34 },
{ user: 'fred', age: 48 },
{ user: 'fred', age: 40 }
];
let f = orderBy2(users, ["user", "age"], ["asc", "desc"]);
来看看 orderBy2
的入参
function orderBy2(collection, iteratees, orders){
}
其中 collection
代表的是 users
定义的数组,iteratees
代表的是 ["user", "age"]
, orders
代表的是 ["asc", "desc"]
首先第一步对 iteratees
格式化
function orderBy2(collection, iteratees, orders){
// xxx
iteratees = iteratees.map(path => obj => obj[path]);
//xxxx
}
经过这一步,iteratees
会转为
iteratees = [
function(obj){
return obj['user']
},
function(obj){
return obj['age']
},
]
第二步,根据 collection
和 iteratees
重新生成一个带有评测的数组
function orderBy2(collection, iteratees, orders){
// xxx
var result = collection.map(function (value) {
// 把 iteratees 值对应 value 值 提取出来
var criteria = iteratees.map(function (iteratee) {
return iteratee(value);
});
//
return { criteria: criteria,value: value };
});
// xxx
}
最后 的 result
的结果是
// [
// {
// criteria: [ 'fred', 48 ],
// value: { user: 'fred', age: 48 }
// },
// {
// criteria: [ 'barney', 34 ],
// value: { user: 'barney', age: 34 }
// },
// ]
第三步,根据 criteria
进行排序
result.sort(function (a, b) {
let r = compareMultiple(a, b, orders);
return r;
});
sort()
方法就地对数组的元素进行排序,并返回对相同数组的引用。默认排序是将元素转换为字符串,然后按照它们的 UTF-16 码元值 升序排序。
这是 sort 的返回值对应的顺序,也就是如果小于 0,就是正序,原因也简单, [1,2,3] 这种 1 - 2 = -1
, 2 - 3 = -1
compareFn(a, b) 返回值 |
排序顺序 |
---|---|
> 0 | a 在 b 后,如 [b, a] |
< 0 | a 在 b 前,如 [a, b] |
=== 0 | 保持 a 和 b 原来的顺序 |
使用自定义 compareMultiple
进行排序,会返回 1 / -1
这样的结果,也正是 sort
所需要的
// [
// {
// criteria: [ 'fred', 48 ],
// value: { user: 'fred', age: 48 }
// },
// {
// criteria: [ 'barney', 34 ],
// value: { user: 'barney', age: 34 }
// },
// {
// criteria: [ 'fred', 40 ],
// value: { user: 'fred', age: 40 }
// },
// {
// criteria: [ 'barney', 36 ],
// value: { user: 'barney', age: 36 }
// }
// ]
function compareMultiple(object, other, orders) {
// order = ["asc", "desc"]
var index = -1,
// 获取对应criteria的数组
objCriteria = object.criteria,
othCriteria = other.criteria,
// 获取 criteria 的长度
length = objCriteria.length,
ordersLength = orders.length;
// 由于有多个 条件,需要
while (++index < length) {
// "barney" , "fred"
let x = objCriteria[index];
let x1 = othCriteria[index];
// b 的码元 小于 f 的码元,所以是 -1
var result = x == x1 ? 0 : x < x1 ? -1 : 1;
if (result) {
var order = orders[index];
// 如果 order 是 "desc" 的话是降序,要 乘以 -1
// 此时返回给 order 的就是 1
return result * (order == "desc" ? -1 : 1);
}
}
}
有兴趣可以看 模拟数据
let result = [
{
criteria: ["fred", 48],
value: { user: "fred", age: 48 },
},
{
criteria: ["barney", 34],
value: { user: "barney", age: 34 },
},
{
criteria: ["fred", 40],
value: { user: "fred", age: 40 },
},
{
criteria: ["barney", 36],
value: { user: "barney", age: 36 },
},
];
// 经过 自定义排序之后的结果
// [
// {
// criteria: [ 'barney', 34 ],
// value: { user: 'barney', age: 34 }
// },
// {
// criteria: [ 'barney', 36 ],
// value: { user: 'barney', age: 36 }
// },
// { criteria: [ 'fred', 40 ], value: { user: 'fred', age: 40 } },
// { criteria: [ 'fred', 48 ], value: { user: 'fred', age: 48 } }
// ]
result.sort(function (a, b) {
let index = -1,
objCriteria = a.criteria,
othCriteria = b.criteria,
length = objCriteria.length,
orders = ["desc", "asc"];
while (++index < length) {
let x = objCriteria[index];
let x1 = othCriteria[index];
var result = x == x1 ? 0 : x < x1 ? -1 : 1;
if (result) {
var order = orders[index];
result * (order == "desc" ? -1 : 1);
break;
}
}
index = -1;
let r = compareMultiple(a, b, ["desc", "asc"]);
return result;
});
function compareMultiple(object, other, orders) {
var index = -1,
objCriteria = object.criteria,
othCriteria = other.criteria,
length = objCriteria.length;
while (++index < length) {
let x = objCriteria[index];
let x1 = othCriteria[index];
var result = x == x1 ? 0 : x < x1 ? -1 : 1;
if (result) {
var order = orders[index];
return result * (order == "desc" ? -1 : 1);
}
}
}
结尾
最后的 orderBy
中的 sort 是有一些复杂,现在还有一些不太懂,为啥 通过 sort 返回 1/-1
可以影响 传入参数 a/b
的顺序,他们之间的具体关系不太明白,有时间再看看