Vincent Ko

VK's Blog

性能向上のテクニック:Setと配列メソッドを使用してJavaScriptコードを最適化する

フロントエンドコードの作成と保守の過程で、パフォーマンス最適化が必要なシーンにしばしば直面します。特に配列やオブジェクトを扱う際には、JavaScript が提供するメソッドを適切に使用することで、コードの実行効率を向上させるだけでなく、コードをより簡潔で理解しやすくすることができます。本記事では、実際のビジネスコードのレビューから、JavaScript の配列と集合メソッドを合理的に活用して最適化を達成する方法を探ります。

ビジネスシーン#

まず、一般的なシーンを見てみましょう。ユーザーが選択した各権限範囲に対して、適切な権限が割り当てられているかを検証する必要があります。この要求は非常に直接的に聞こえますが、実際の実装では、一連の配列操作が関与する可能性があります。これが今日最適化するソースコードです:

// 初期の権限検証ロジック
const selectedObjItem = [];
this.selectedPermissions.forEach((item) => {
  !selectedObjItem.includes(item.action_id) && selectedObjItem.push(item.action_id);
});

const result = this.selectedScopes.every(scope => {
  if (scope.has_instance) {
    return selectedObjItem.includes(scope.action_id);
  }
  return true;
})

if (!result) {
  showMsg(this.$t('AUTH:オブジェクト範囲を選択してください'), 'warning');
  return false;
}

このコードの目的は、選択された各シーン (selectedScopes) が権限範囲を選択しているかどうかを検証し、これらの権限範囲が selectedPermissions に一致する必要があることです(各権限オブジェクトには一意の識別子 action_id があります)。

コードは表面的には期待通りの効果を達成していますが、詳細に分析すると最適化の余地があることがわかります。実際、いくつかの簡単な変更を加えることで、実行効率を向上させ、コードをより明確にすることができます。そのために、以下のいくつかの問題を考慮することができます:

  1. 配列の記録と重複排除を実現するために、より効率的な方法はありますか?
  2. every は選択を検証するための最良の方法ですか?
  3. 最終的な結果の判断は、すべての遍歴が終了するまで待つ必要がありますか?

最適化#

1. 重複排除と記録#

最初の記録は実際には重複排除も含まれており、主に push 前の証拠判定: !selectedObjItem.includes(item.action_id) に依存しています。重複排除が関与する場合、ES6 の自然で効率的なデータ型 Set を使用することを考えることができます。なぜなら、Set 自体が重複排除の特性を持っているからです。したがって、コードは次のように最適化できます:

const selectedObjItem = new Set();
this.selectedPermissions.forEach((item) => {
  selectedObjItem.add(item.action_id);
});

Set を使用して重複を排除し、has メソッドを使用して要素の存在を確認することは、通常、配列の重複排除(pushincludes を組み合わせて確認する方法)よりも効率的です。その理由は次のとおりです:

  1. 時間計算量
    • Setadd メソッドと has メソッドの時間計算量は通常定数時間計算量 O (1) です。これは、Set が内部的にハッシュテーブルを使用しているため、データの迅速な検索と挿入が可能だからです。
    • 配列の includes メソッドの時間計算量は O (n) です。最悪の場合、特定の要素が存在するかどうかを判断するために、配列全体を遍歴する必要があります。
  2. 重複排除
    • Set データ構造は、設計上、重複しない要素の集合です。要素の一意性を自動的に管理します。すでに Set に存在する要素を追加しようとすると、Set は何も操作を行わないため、手動で重複排除のロジックを書く必要はありません。
    • 配列で重複を排除する場合は、追加する要素が一意であることを保証するために、追加のロジック(通常は includes を使用してから push)を書く必要があります。これにより、コードの複雑性と実行の操作ステップが増加します。
  3. コードの簡潔性
    • Set を使用すると、コードがより簡潔で理解しやすくなります。Set のインターフェースは明確な意図を持ち、集合というデータ構造を表しているため、コードの意図がより明確で、意味が良くなります;
    • 配列の重複排除に関与する includespush メソッドは、どちらも配列メソッドであり理解しやすいですが、重複排除を実現するために組み合わせて使用する必要があるため、コードが直感的ではなくなります。

Set の使用と応用については、[下文](## 附:Set の説明与用例) で紹介します。

2. 検出の最適化#

ソースコードで every メソッドを使用することは可能ですが、同時に非効率的です。なぜなら、every はすべての項目を遍歴する必要があり、今回の検証の目的はすべての項目が選択されていることなので、実際には 1 つでも選択されていないものがあれば、早期にエラー結果を返すことができ、後続の内容を確認する必要はありません。

このような考え方があれば、some メソッドを思い出すのは簡単です。コードを次のように最適化できます:

const result = this.selectedScopes.some(scope => {
  if (scope.has_instance) {
    return !selectedObjItem.has(scope.action_id);
  }
  return false;
});

コードのロジックと構造は元のものと変わらないように見えますが、実際には効率が向上します。これは、sometrue を返した後、後続のループを続行しないためです。

3. 完全なコード#

最適化された完全なコードは次のとおりです:

// 重複排除して action_id を記録
const selectedObjItem = new Set();
this.selectedPermissions.forEach((item) => {
  selectedObjItem.add(item.action_id);
});

// すべての選択肢が選択されているかを検出
const result = this.selectedScopes.some(scope => {
  if (scope.has_instance) {
    return !selectedObjItem.has(scope.action_id);
  }
  return false;
});

if (result) {
  showMsg(this.$t('AUTH:オブジェクト範囲を選択してください'), 'warning');
  return false;
}

シーントレーニング#

似たような例を見てみましょう:

batchAddSource は二次元配列で、その値は [['a','b'], ['c','d'], ...] です;selectedListbatchAddSource のサブセットで、選択された内容を示します。現在、選択された内容を渡すと未選択の内容を返すメソッド getUnselectedList が必要です。

思考#

この問題の核心は重複排除の問題であり、前述の最適化の考え方を組み合わせると、以下のようなアイデアが考えられます:

  1. 二次元配列の比較では、内側の配列をより比較しやすい文字列型(join メソッド)に変換できます。
  2. Set データ構造の has メソッドを使用して存在を判断します。
const getUnselectedList = (selectedList, batchAddSource) => {
	const selectedSource = new Set(selectedList.map(item => item.join('.')))
	return batchAddSource.filter(item => !selectedSource.has(item.join('.')))
}

まとめ#

JavaScript は進化し続ける言語であり、ES6 バージョンは新しい構文と機能を導入し、コード作成の効率と可読性を大幅に向上させました。ここでは、言及された幾つかのメソッドと応用を簡単にまとめます。

  1. 新しいデータ構造 Set の使用:配列の重複排除と非重複値の効率的な記録機能を非常に便利に実現できます。従来の遍歴チェック方法と比較して、Set のネイティブメカニズムは集合内の一意性を保証し、パフォーマンスの最適化に顕著な役割を果たします。特に大量のデータを処理する際には、効率を大幅に向上させることができます。
  2. 配列メソッドの合理的な使用:ES6 は forEacheverysome などの配列メソッドを提供しており、これらのメソッドを合理的に使用することで、コードをより明確にし、異なるビジネスロジックの要求に応じて最適な反復方法を選択できます。例えば、some を使用して早期に結果を返すことで、不必要な遍歴を避けることができます。
  3. 論理判断の最適化:論理的な最適化を通じて、早期にループを終了することで、不必要な計算を避け、リソースと時間を節約できます。配列や集合の検証操作を実装する際には、境界条件や短絡論理を考慮することで、より効率的なコード実行が可能になります。
  4. ES6 の簡潔な構文の活用:アロー関数、テンプレート文字列などの新機能を使用することで、コードをより簡潔にし、冗長なコードを避け、コードの理解しやすさを向上させることができます。

実際のプログラミング実践において、各最適化ポイントは微小なパフォーマンス向上をもたらすかもしれませんが、全体としてこれらの改善はアプリケーションのパフォーマンスとユーザー体験を大幅に向上させることができます。上記の最適化手段を総合的に活用することで、効率的で良好な可読性を持つ JavaScript コードを作成することができます。

附:Set の説明と用例#

Set は特別なタイプの集合であり、それぞれの値は一度だけ現れることができます

上文で Set の適用シーンを紹介しましたが、ここではこのデータ型の特性を簡単に振り返ります。主なメソッドは次のとおりです:

  • new Set(iterable) —— set を作成します。iterable オブジェクト(通常は配列)が提供された場合、配列から値を set にコピーします。
  • set.add(value) —— 値を追加し、set 自体を返します。
  • set.delete(value) —— 値を削除します。このメソッドが呼び出されたときに value が存在する場合は true を返し、そうでない場合は false を返します。
  • set.has(value) —— valueset に存在する場合は true を返し、そうでない場合は false を返します。
  • set.clear() —— set を空にします。
  • set.size —— 要素の個数を返します。

主な特徴は、同じ値を set.add(value) で再度呼び出しても何も変わらないことです。これが Set の中の各値が一度だけ現れる理由です。

カウント(統計)#

以下のコードを見て、コメントなしで何をしているのか、関連する原理を理解できるか確認してください:

const uid = () =>
  String(
    Date.now().toString(32) +
      Math.random().toString(16)
  ).replace(/\./g, '')

const size = 1000000
const set = new Set(new Array(size)
  .fill(0)
  .map(() => uid()))

console.log(
  size === set.size ? 'すべてのIDは一意です' : `重複レコード ${size - set.size}`
)

そうです、実際には uid 関数を検証して、生成された ID が一意であるかどうかを確認しています。興味深いのは、ここでどのように Set を利用して一意性を判断しているかです。なぜ set.size === size で生成された UUID が一意であるかを判断できるのでしょうか?

それは、Set の集合内の値は一度だけ現れるため、map を使用して uid() を生成する際に、同じ値があれば集合に追加されないからです。この特性を利用して、最初の例で重複する値があるかどうか、また重複の数を統計することができます:

  • set.size を呼び出すと、すべてが一意であれば、map 後の集合の長さと最初に定義した size の長さが一致します;
  • そうでなければ、sizeset.size の差が重複の数です。

Set を合理的に使用することで、コードをより効率的にし、より洗練された理解しやすいコードにすることができます。したがって、重複排除、統計、カウントなどのシーンに遭遇した場合、以前はためらうことなく配列を使用していた場合でも、Set を使用してコードの効率を向上させることができるかどうかを考慮することが重要です。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。