lodash


将lodash中比较常用且原生写法比较复杂的,或是官方文档(中文版)中描述不是很易懂的方法记录下来。

数组篇

// 原数组
let a = [1, 2, 3, 4, 5, 4, 3, 2, 1]

dropWhile

官方的描述和例子都让人很疑惑,不如方法名实在——左过滤和右过滤
参数:
1、原数组
2、过滤条件

_.dropWhile(a, (v) => v < 3)
// return [3, 4, 5, 4, 3, 2, 1]
_.dropRightWhile(a, (v) => v < 3)
// return [1, 2, 3, 4, 5, 4, 3]

该方法是有break的,实用价值应该不大。

difference

实为filter三连

  • difference(array, [filter])
    直接判断是否与过滤数组中的元素相等
    _.difference(a, [2, 3])
    // [1, 4, 5, 4, 1]
    
  • differenceBy(array, [filter], iteratee)
    比较前用iteratee包装,再比较是否相等
    _.differenceBy(a, [1.1, 2.1], Math.floor)
    // return [3, 4, 5, 4, 3]
    
    这里a中的元素也会用Math.floor处理一遍,不过原数组都是整数,所以看不出来
  • differenceWith(array, [filter], comparator)
    与differenceBy不同,这个更贴近于difference,comparator接收原数组和过滤数组的元素,比较进行返回,也就是不限制只能等于。
    _.differenceWith(a, [2.1, 4.1], (v1, v2) => v1 > v2)
    // [1, 2, 2, 1]
    
    v1是原数组元素,v2是过滤数组元素,这里应该是个嵌套循环,每个a.ele都需要满足comparator返回false才能不被过滤,比如3,只满足3 > 4.1为false,不满足3 > 2.1为false,所以被过滤了。

注:这里可以看出来lodash函数的命名风格,后面也有类似的——funName是原功能,funNameBy是(用iteratee)包装元素,funNameWith是自定义比较规则,一般用于比较对象数组。
总结:实用性来看,difference会比较有用,其他情况下(对象数组的复杂过滤)我更可能想到的是原生实现。

flatten

对比原生的flat:
_.flatten(a) == a.flat()
_.flattenDeep(a) == _flattenDepth(a, Infinity) == a.flat(Infinity)
_flattenDepth(a, n) == a.flat(n)
所以用flat还少打点字,且压平数组的使用场景,目前还没见过。

pull

删除方法,所有相关方法均会改变原数组

  • pull
    _.pull(a, 3, 4)
    // [1, 2, 5, 2, 1]
    
    要删除的元素依次用,隔开
  • without
    参数规则同pull,但是不会改变原数组
    _.without(a, 3, 4)
    // [1, 2, 5, 2, 1]
    // a [1, 2, 3, 4, 5, 4, 3, 2, 1]
    
  • pullAll
    _.pullAll(a, [3, 4])
    // [1, 2, 5, 2, 1]
    
    要删除的元素是一个数组,与call & apply类似。
    pullAllBy与pullAllWith与前者同。
  • pullAt
    _.pullAt(a, 0, 2)
    // return [1, 3]
    // 原数组变为 [2, 4, 5, 4, 3, 2, 1]
    
    与前面还是有点区别,参数是删除的元素索引,返回的是删除的元素。
  • remove
    _.remove(a, v => v & 1)
    // return [1, 3, 5, 3, 1]
    // 原数组变为 [2, 4, 4, 2]
    
    同xxxWith,参数是比较规则,这里规则是奇数;同pullAt,返回值是删除的元素,原数组会变。
    pull系列实用价值很不错,用起来相比于原生实现会简短很多。

take

按关联性来说,应该放到drop后面,即过滤的反面,笔记顺序按照官网列的来。
本系列所有方法均不改变原数组。

  • take(arr, n = 1)
    相当于a.slice(0, n)
    _.take(a)
    // [1]
    _.take(a, 3)
    // [1, 2, 3]
    
  • takeRight(arr, n = 1)
    相当于a.slice(-n)
    _.take(a)
    // [1]
    _.take(a, 3)
    // [3, 2, 1]
    
  • takeWhile & takeRightWhile(arr, callback)
    当cb返回true时take,返回false时break。
  • tail(a)
    即slice(1),不同于shift,这个不改变原数组。
    总结:slice的封装版,可能用惯了slice还不太习惯用这些。

union

之前有个求交集的,intersection,没记录,感觉用处不大,这个是用来求并集且去重的。

  • union([arrs])
    参数无限制长度。
    _.union([1,2,5], [1,2,2,4])
    // [1, 2, 5, 4]
    
    有By和With,不赘述。

uniq

  • uniq(arr)
    去重,同[…new Set(arr)]
    _.uniq(a)
    // [1, 2, 3, 4, 5]
    
    有By和With,不赘述。

zip

  • zip([arrs])
    不太好描述,有点像矩阵翻转。
    _.zip(['fred', 'barney'], [30, 40], [true, false])
    // [['fred', 30, true], ['barney', 40, false]]
    _.zip(['fred', 'barney'], [30, 40, 50], [true, false, true, false])
    // [["fred", 30, true], ["barney", 40, false], [undefined, 50, true], [undefined, undefined, false]]
    
    如果出现分组的数组长度不一致的情况,以最长的长度为结果数组的长度,分配元素arrN[i],没有自然是undefined。
  • unzip([arrs])
    与zip相反的作用
    _.unzip([['sv', 1, 'kk'], ['bs', 2, 'ss'], []])
    // [["sv", "bs", undefined], [1, 2, undefined], ["kk", "ss", undefined]]
    
  • zipObject(arr1, arr2)
    这种日常开发很常见,一个是Object.keys,一个是Object.values,这个方法是重组object。
    _.zipObject(['a', 'b'], [1, 2])
    // {a: 1, b: 2}
    _.zipObject(['a', 'b'], [1, 2, 3])
    // {a: 1, b: 2}
    _.zipObject(['a', 'b', 'c', 'd'], [1, 2, 3])
    // {a: 1, b: 2, c: 3, d: undefined}
    
    结果对象按keyArr.length算,如果keyArr.length < valArr.length,多的值舍去,反之则值用undefined填补。
  • zipObjectDeep(arr1, arr2)
    加了深度的重组object,这种情况不常见,遇到了我应该也会自己遍历。
    _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2])
    // { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
    
  • zipWith([arrs], iteratee)
    with类作用都一样,这里列举一种可能的使用场景。
    _.zipWith(['Joe', 'John'], [22, 25], [8000, 10000], function(a, b, c, d) {
    return `My name is ${a}, ${b} years old, salary: ¥${c}.`;
    });
    // ["My name is Joe, 22 years old, salary: ¥8000.", "My name is John, 25 years old, salary: ¥10000."]
    
    当某类的实例属性被分组时,可以这样重组使用。
  • unzipWith([[arrs]], iteratee)
    类似zipWith,区别只在于参数1用数组包起来了,类比apply,call。
    注:如果没有iteratee,unzipWith == upzip,zip同理。
    _.unzipWith([[1, 10, 100], [2, 20, 200]], _.add)
    // [3, 30, 300]
    _.zipWith([1, 10, 100], [2, 20, 200], _.add)
    // [3, 30, 300]
    
    总结:感觉上zipObject和zipWith会比较有用,其他的用处不大。

xor

symmetric difference,对查等分,指由所有只在两个集合其中之一的元素组成的集合。也就是:先求全集,再剔除交集。

_.xor([1, 2, 3], [1, 2, 4])
// [3, 4]
_.xor([1, 2, 3, 3], [1, 2, 4, 5])
// [3, 4, 5]
// 结果是去重的

有By和With方法。实用性不高。

集合篇

看起来还是数组,collection不知道还包含其他什么类型,Set不行。

countBy(col, iteratee)

类似count+groupBy。

_.countBy([1, 2, 1])
// {1: 2, 2: 1}
_.countBy([1.1, 2.3, 1.4], Math.floor)
// {1: 2, 2: 1}
_.countBy(['one', 'two', 'three'], 'length')
// {3: 2, 5: 1}

这里总结一下单属性作为iteratee
// propName == (ele) => ele[propName]


文章作者: kinoko
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kinoko !
  目录