lodashの配列差分ソースコード解析

作用と使い方

baseDifference関数は、指定された配列と別の配列の差分を取得するために使用されます。

この関数は内部関数であり、後で他の比較関数を実装するための核心関数です。

baseDifferenceのメソッドシグネチャは以下の通りです:

baseDifference(array, values, iteratee, comparator)

第一と第二のパラメータは比較する二つの配列です。iterateeは値をマップする関数で、比較時にマップされた値を使用して比較できます。comparatorはカスタム比較関数で、渡された場合はカスタム比較関数を使用して交差の比較を行います。

依存関係

import SetCache from './SetCache.js'
import arrayIncludes from './arrayIncludes.js'
import arrayIncludesWith from './arrayIncludesWith.js'
import map from '../map.js'
import cacheHas from './cacheHas.js'

ソースコード解析

const LARGE_ARRAY_SIZE = 200
function baseDifference(array, values, iteratee, comparator) {
  let contains = arrayIncludes
  let isStandard = true
  const result = []
  const valuesSize = values.length

  if (!array.length) {
    return result
  }
  if (iteratee) {
    values = map(values, (item) => iteratee(item))
  }
  if (comparator) {
    contains = arrayIncludesWith
    isStandard = false
  }
  else if (values.length >= LARGE_ARRAY_SIZE) {
    contains = cacheHas
    isStandard = false
    values = new SetCache(values)
  }
  outer:
  for (let item of array) {
    const computed = iteratee == null ? item : iteratee(item)

    item = (comparator || item !== 0) ? item : 0
    if (isStandard && computed === computed) {
      let valuesIndex = valuesSize
      while (valuesIndex--) {
        if (values[valuesIndex] === computed) {
          continue outer
        }
      }
      result.push(item)
    }
    else if (!contains(values, computed, comparator)) {
      result.push(item)
    }
  }
  return result
}

iterateeの呼び出し

iterateeが渡された場合、まずmapを呼び出し、iterateeを使用して比較する配列のマップ配列valuesを生成します。

後でネストされたループがあるため、iterateeの繰り返し呼び出しによるパフォーマンスへの影響を避けるために、最初にvaluesのマップ配列を生成する必要があります。

パフォーマンス最適化

ここではisStandardを使用して、標準の方法で処理するかどうかを示します。

比較関数が渡された場合、isStandardfalseにマークし、標準の方法で処理しないことを示します。後でcontainsメソッド、つまりarrayIncludesWithメソッドを使用して処理することがわかります。

カスタム比較方式を使用する必要がなく、配列が大きい場合(ここでは200に限定)、SetCacheクラスを使用して配列をキャッシュします。

SetCacheは実際にはMap/Setまたはオブジェクトを使用して配列を保存し、大配列のネストされたループによるパフォーマンスの損失を避けます。

ループ比較

次に、最初の配列arrayを反復処理し、配列の各項目を第二の配列の各項目と比較します。

isStandardfalseにマークされていない場合、または比較する値computedNaNでない場合、ネストされたループを使用して比較します。ループが完了し、第二の配列で同じ項目が見つからない場合、その項目を配列resultに格納します。

isStandardfalseまたは比較する値がNaNの場合、containsメソッドを呼び出して比較します。

以前の分析によると:

  • comparatorを指定した場合、containsarrayIncludesWithになります
  • 比較する配列valuesの長さが200を超える場合、containscacheHasになります
  • それ以外の場合、containsarrayIncludesになります

+0と-0の処理

コードを見ると、非常に奇妙な部分があります:

item = (comparator || item !== 0) ? item : 0

このコードの意味は、comparatorを提供しない場合、item === 0の場合、item0に代入することです。

item === 0の場合、+0-0、または0になる可能性があります。なぜlodashはそれらをすべて0に変換するのでしょうか?

後で、lodashの作者がissueで述べたところによると、比較にSetを使用するためで、Set+0-0を区別できないからです。

タグ: lodash 配列 差分 javascript ソースコード

7月10日 22:33 投稿