必威体育Betway必威体育官网
当前位置:首页 > IT技术

difference()、differenceBy()、differenceWith()

时间:2019-09-30 13:15:37来源:IT技术作者:seo实验室小编阅读:61次「手机版」
 

difference

_.difference()、 _.differenceBy()、 _.differenceWith()

为何将_.difference()_.differenceBy()_.differenceWith()三个方法放在一起分析呢?因为它们的内部都是基于basedifference()方法进行处理,只不过是传入baseDifference()的参数不同罢了。

下面是difference.js、differenceBy.js、differenceWith.js文件的内容

// difference.js
function difference(array, ...values) {
  // 判断array是否是个类数组对象,如果是,则调用baseDifference方法处理,如果不是,则返回空数组
  // 在调用baseDifference处理前,通过调用baseFatten方法将传入的values扁平化处理,关于baseFlatten扁平化处理的方法可见baseFlatten源码解析
  // 从baseFlatten传入的参数可知:扁平化处理的层级为1,遍历values数组中的元素时每次都会调用isArrayLikeObject方法判断元素是否是类数组对象,且是严格模式下扁平化处理
  // baseDifference方法的比较可见baseDifference源码解析
  return isArrayLikeObject(array)
    ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
    : []
}
// differenceBy.js
function differenceBy(array, ...values) {
  // 取values数组中的最后一个元素为迭代器iteratee
  let iteratee = last(values)
  // 如果迭代器iteratee是个类数组对象,则迭代器iteratee为undefined
  if (isArrayLikeObject(iteratee)) {
    iteratee = undefined
  }
  // 如果array是类数组,则调用baseDifference方法,baseDifference参数为array、扁平化处理的values以及迭代器iteratee函数,否则返回空数组
  // 这块可能会有个疑问,就是如果values数组的最后一个元素为迭代器iteratee函数,那么在扁平化的时候是不是把这个函数也扁平化进去?答案是no,baseFlatten中第三个参数就是帮我们将非类数组类型的元素过滤掉
  return isArrayLikeObject(array)
    ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), iteratee)
    : []
}
// differenceWidth.js
function differenceWith(array, ...values) {
  // 取values数组的最后一个元素为比较器comparator
  let comparator = last(values)
  // 如果比较器comparator是非数组类型,则比较器comparator为undefined
  if (isArrayLikeObject(comparator)) {
    comparator = undefined
  }
  // 如果array为类数组对象,则调用baseDifference方法,baseDifference的参数依次为array、扁平化处理的values数组、undefined、比较器comparator,否则返回空数组
  return isArrayLikeObject(array)
    ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator)
    : []
}

从上面的代码中可以看出当array为类数组时,调用baseDifference方法,那么baseDifference方法是什么呢?

// baseDifference.js
// baseDifference方法用来将array数组与values数组进行对比,将存在于两者之中的元素从array数组中剔除掉,array中剩余的值组成一个新数组返回
// 对比的时候可以传入迭代器iteratee函数,array和values数组中的每个元素都会调用迭代器iteratee进行处理,然后chubaseDifference对比处理后的值
// 对比的时候也可以传入比较器函数,在对比的时候调用comparator来比较array和values中每个元素,可以理解为比较器comparator定义了对比的规则,默认是看两个值是否相等
// baseDifference方法会接收4个参数,依次为需要处理的array数组、用来对比的values数组、迭代器iteratee、比较器comparator
// 迭代器iteratee是个function,array和values中的每个元素都需要调用该方法处理
function baseDifference(array, values, iteratee, comparator) {
  // includes用于储存判断数组中是否存在某个值的方法
  let includes = arrayIncludes
  // isCommon用于区分是普通对比,还是特殊对比
  let isCommon = true
  const result = []
  const valuesLength = values.length

  // array为空直接返回空数组
  if (!array.length) {
    return result
  }
  // 如果iteratee存在,则遍历values并对其中每个元素都调用iteratee方法
  if (iteratee) {
    values = map(values, (value) => iteratee(value))
  }
  // 如果存在比较器comparator,则为特殊对比,includes为arrayIncludesWith,其中arrayIncludesWith方法中就可以传入比较器comparator
  if (comparator) {
    includes = arrayIncludesWith
    isCommon = false
  }
  else if (values.length >= LARGE_ARRAY_SIZE) {
    // LARGE_ARRAY_SIZE是个常量,值为200,values长度超过200,则为特殊处理,includes为cacheHas
    includes = cacheHas
    isCommon = false
    values = new SetCache(values)
  }

  // 遍历array数组,
  outer:
  for (let value of array) {
    // 如果存在iteratee,用iteratee处理value
    const computed = iteratee == null ? value : iteratee(value)

    value = (comparator || value !== 0) ? value : 0
    // 如果isCommon为true且computed === computed时遍历values,判断values中的元素是否有与computed相同的,如果没有则将当前value添加result中
    if (isCommon && computed === computed) {
      let valuesIndex = valuesLength
      // 遍历values,当values中有元素与computed相同时,跳出当前array的循环,继续进行array的下一个循环,这样可以减少不必要的循环
      // 只有当遍历完values中所有的元素后,如果都没有与computed相同的,说明当前value是array独有的,那么将value添加到result中
      while (valuesIndex--) {
        if (values[valuesIndex] === computed) {
          continue outer
        }
      }
      result.push(value)
    }
    else if (!includes(values, computed, comparator)) {
      // isCommon为false的条件是存在comparator或者values.length超过200,此时会调用includes进行判断
      // 如果includes返回的结果为false,则表明在比较器comparator的规则下,values中包含computed,此时需将value添加到result中
      result.push(value)
    }
  }
  // 返回结果result
  return result
}

实现思路:

difference、differenceBy、differenceWith方法都可以将一个数组(array)与多个数组(array1、array2、array3 …)进行对比,以剔除其中共同存在的元素,在与多个数组(array1、array2、array3 …)对比的时候,利用数组扁平化处理,将多个数组(array1、array2、array3 …)整合成一个数组(values),然后进行比较;

比较的时候,根据传入参数的不同采用不同的比较方法,对于difference方法,在对比array数组和整合数组values时,采用双层循环对比的一种方法,在外层遍历array数组,内部遍历values数组,判断values是否存在array数组中的元素,对于values中不存在array数组中的元素,则添加到新数组中返回,其中在双层循环时采用了标签语法,就是源码中的outer和continue outer语句,这样写的好处就是当values数组中存在array中的元素时就跳出当前循环,继续array的下一次循环,减少不必要的变量;

对于differenceBy方法,它的实现跟difference相似,不同的地方在于:difference在对比array和values数组时是对比数组中的原始数据,而differenceBy则是对array和values数组中的每个元素调用迭加器函数进行处理,然后对比处理后的值;

对于differenceWith方法,与前两种有些不太一样,前两种都是默认比较array和values中元素是否相同,而differenceWith通过传入一个比较器来自定义比较规则,differenceWith也是基于双层循环,外层循环array数组,内层遍历values数组,内层遍历时不再是简单的对比values中值与array的元素是否相同,而是在比较器的规则下将values中的值与array中的值对比,比较器返回true的表明array中该值需要剔除,否则会添加到新数组返回;

看完上面的长篇文字后,可以通过几个例子来看一下结果:

example
_.difference([1, 3, 5], [1, [2, 3]], [4, 6])
// [1, [2, 3]], [4, 6]扁平化处理后得到[1, [2, 3], 4, 6],对比后结果为[3, 5]
// [3, 5]
_.difference([1, [2, 3], 5], [1, [2, 3]], [4, 6])
// [1, [2, 3]], [4, 6]扁平化处理后得到[1, [2, 3], 4, 6],由于两个数组中的[2, 3]不是同一个引用,所以对比后结果为[[2, 3], 5]
// [[2, 3], 5]
_.differenceBy([1.1, 3.6, 5.2], [1.6, 2.3, 3.7], Math.floor)
// 两个数组中每个元素经过Math.floor处理后变成[1, 3, 5]和[1, 2, 3],最终对比结果为[5.2]
// [5.2]
function customCompare(a, b) {
    return a - b === 1
}
_.differenceWith([1, 3, 5], [1, 4, 3], customCompare)
// [1, 3, 5]中的每个元素与[1, 4, 3]比较,只有相差1的时候customCompare返回true,即不需要添加到新数组中返回,可以看出[1, 3, 5]中只有5与[1, 4, 3]中的4比较时差1,最终对比结果为[1, 3]
    // [1, 3]

其实对于difference()、differenceBy()、differenceWith()方法的源码解析,里面涉及到一些其他方法,如:isArrayLikeObject、baseFlatten、arrayIncludes、arrayIncludesWith、cacheHas等等方法,我们在这儿没有详细展开,仅仅简单的介绍了它们的作用,因为我们主要分析的是difference实现的逻辑,其他的在这儿就不是重点了(看源码,主次要分明),不过这些方法会在后面需要的时候再进一步详细介绍;

遗留一个问题,就是上面baseDifference方法中,有个computed === computed的判断,一直不明白为什么,难道还会有computed与computed不等的情况?

2019/6/19

相关阅读

Difference

题目链接 题意 定义 f(y,k)f(y,k)f(y,k) 为 yyy 各个数位上数字的 kkk 次方和,问有多少个不同的 yyy 满足 x=f(y,k)−yx=f(y,k)-y

分享到:

栏目导航

推荐阅读

热门阅读