lodash-14 keyBy,orderBy

前言

今天是 lodash-14,今天带来的是 keyByorderBy

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

  1. collection  (Array|Object) : The collection to iterate over.
  2. [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

  1. collection  (Array|Object) : The collection to iterate over.
  2. [iteratees=[_.identity]]  (Array[]|Function[]|Object[]|string[]) : The iteratees to sort by.
  3. [orders]  (string[]) : The sort orders of iteratees.

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']
    },
]

第二步,根据 collectioniteratees 重新生成一个带有评测的数组

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;
});

MDN-sort

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 的顺序,他们之间的具体关系不太明白,有时间再看看

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYYnkxvq' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片