JavaScript インタビュー: 配列の平坦化メソッドを実装する方法

JavaScript インタビュー: 配列の平坦化メソッドを実装する方法

1 配列のフラット化とは何ですか?

概念は単純で、「多次元」配列の次元を減らすことを意味します。たとえば、次のようになります。

// 元の配列は「3次元」配列です。const array = [1, 2, [3, 4, [5, 6], 7], 8, 9]
 
// 2次元に縮小できます newArray1 = [1, 2, 3, 4, [5, 6], 7, 8, 9]
 
// 1次元に縮小することもできます newArray2 = [1, 2, 3, 4, 5, 6, 7, 8, 9]

配列の平坦化は、配列の平坦化や配列の次元削減とも呼ばれます。

2 JS標準ライブラリの配列フラット化メソッド

JavaScript標準ライブラリは配列を平坦化するメソッドArray.prototype.flat()を実装しています。

flat() メソッドは、配列を指定された深さまで再帰的に走査し、走査されたサブ配列内のすべての要素を新しい配列にマージして返します。

構文: var newArray = arr.flat([depth])

パラメータ: depth はオプションの値で、走査する多次元配列の深さを示します。デフォルト値は 1 です。これは、拡張(または次元を縮小)するレイヤーの数として理解できます。

戻り値: 走査された要素とサブ配列の要素で構成される新しい配列

例:

定数配列 = [1, 2, [3, 4, [5, 6], 7], 8, 9]
const newArray1 = array.flat() // array.flat(1) と同等。1 次元減らす // newArray1: [1, 2, 3, 4, [ 5, 6 ], 7, 8, 9]
 
const newArray2 = array.flat(2) // 2次元を削減 // newArray2: [1, 2, 3, 4, 5, 6, 7, 8, 9]

特別:

深さ<=0の場合、返される配列は元の配列と同じ次元を持ちます(次元のみが同じであることに注意してください。空きスペースについてはポイント3を参照してください)。

定数配列 = [1, 2, [3, 4, [5, 6], 7], 8, 9]
配列.flat(-1)
// [1, 2, [3, 4, [5, 6], 7], 8, 9]

深さ=Infinityの場合、返される配列は1次元になります

配列.flat(無限大)
// [1、2、3、4、5、6、7、8、9]

元の配列に空がある場合、flat メソッドはそれを削除します。flat(0) でも空は削除されますので、1 つ目のポイントは「次元数だけが同じである」ということです。そして、平面方式が拡張したレベルでは空きスペースが解消され、より深いレベルの空きスペースは解消されません。

定数配列1 = [1, , 2, [3, ,4, [5, 6], 7], 8, 9] 
// この配列には2つの空きスペースがあることに注意してください array.flat(0)
// [ 1, 2, [ 3, ,4, [ 5, 6 ], 7 ], 8, 9 ]
// 最初の空きはなくなったが、2番目の空きはまだある

3 フラットメソッドの実装

1つのレイヤーを拡張する(1つの次元を減らす)フラットメソッドの手順は次のとおりです。配列を走査し、現在の要素が配列であるかどうかを判断します。配列でない場合は直接保存します。配列の場合は、展開して保存します。

flat メソッドは、1 つのレイヤーの拡張に基づいて、配列のサブ要素に対して同じ操作を再帰的に実行することにより、複数のレイヤーを拡張します (複数の次元を削減します)。

この方法は、次の 3 つのステップに分けられます。

1. 配列を走査する方法

2. 要素が配列であるかどうかを判断する方法

3. 再帰

上記の3つのステップを実装し、それらを組み合わせてさまざまなフラット実装を取得します。

3.1 配列を走査する方法

方法は多数ありますが、ここでは 3 つのカテゴリを紹介します。

1. 関連

  • forループ
  • ...のために

for...in はオブジェクトのプロパティを反復処理するために構築されており、配列での使用は推奨されません。

定数配列 = [1, 2, [3, 4, [5, 6], 7], 8, 9]
// for ループ for (let i = 0; i < array.length; i++) {
    const要素 = 配列[i];
}
// 〜の〜
for (配列のconst要素) {
    
}

2. 配列メソッド:配列要素を直接取得できるメソッド

  • 各()
  • 減らす()
  • 地図()
// 各()
配列.forEach(要素 => {
    
});
// 減らす()
配列.reduce((pre, cur) => {
    const 要素 = cur
}, [])
// マップ()
配列.map(要素 => {
  
})

3. 配列メソッド: イテレータオブジェクトを返すメソッド

  • キー()
  • 値()
  • エントリ()
// これら 3 つのメソッドは、トラバーサル オブジェクトを取得するためだけのものであり、トラバーサルを行うには for...of と一緒に使用する必要があります // keys()
for (let i of array.keys()) {
  const 要素 = 配列[i]
}
// 値()
for (let 配列の要素.values() ) {
  
}
// エントリ()
for (let [i, 要素] of array.entries()) {
  コンソール.log(配列[i])
  console.log(要素)
}

3.2 要素が配列であるかどうかを判断する方法

変数 a があると仮定し、それが配列かどうかを判断します。 4つの方法があります:

  • 配列には、変数が配列であるかどうかを判断するために使用される静的メソッド Array.isArray() があります。
  • instanceof 演算子は、コンストラクター関数のプロトタイプ プロパティがインスタンス オブジェクトのプロトタイプ チェーンに出現するかどうかを検出するために使用されます。
    が配列の場合、Array.prototype がそのプロトタイプ チェーンに表示されます。
  • オブジェクトのコンストラクタによって決定されます (コンストラクタは手動で変更できるため、このメソッドは失敗する可能性があります)
  • Object.prototype.toString()から判断すると、このメソッドはオブジェクトを表す文字列を返すことができる。
// 方法 1
配列.isArray(a)
// 方法 2
配列のインスタンス
// 方法 3
a.コンストラクタ === 配列
// 方法 4
// call を使用して Object.prototype の toString メソッドを呼び出します。Object.prototype.toString.call(a) === '[object Array]'
 
// この toString はすでに Object.prototype.toString を上書きしているため、判断できません
// Object.prototype.toString のみが正しく型を判別できます a.toString()

3.3 再帰

再帰: 子要素に対して同じ操作を実行する

関数flat() {
  res = [] とします
  配列を走査する {
    if (現在の要素が配列の場合) {
      flat(現在の要素)は1次元配列を取得し、その1次元配列をresに連結します} else {
      res.push(現在の要素)
    }
  }
  戻り値
}

3.4 フラットメソッドの予備実装

トラバーサル方式と配列判定方式を選択することで、次のように再帰によるフラット方式を事前に実装できます。

関数 myFlat(arr) {
  res = [] とします。
  for (arrのconst項目) {
    Array.isArray(item) の場合 {
      res = res.concat(myFlat(item));
      // concat メソッドは新しい配列を返し、元の配列を変更しないことに注意してください} else {
      res.push(アイテム);
    }
  }
  res を返します。
}

myFlat メソッドは、多次元配列を 1 次元配列に平坦化できますが、拡張の深さを指定することはできず、空の配列を処理することもできません。

4 最適化

4.1 拡張深さの指定

拡張深度を処理するのは実際には非常に簡単です。再帰終了条件、つまり depth<=0 を追加できます。コードは次のとおりです。

関数 myFlat(arr, 深さ = 1) {
  // 深さ <= 0 の場合は直接戻ります if (depth <= 0) {
      リターン
  }
  res = [] とします。
  for (arrのconst項目) {
    Array.isArray(item) の場合 {
      // 再帰呼び出しごとに深さが1ずつ増加します
      res = res.concat(myFlat(item, depth - 1));
    } それ以外 {
      res.push(アイテム);
    }
  }
  res を返します。
}

4.2 アレイの空き領域の処理

実際、空の配列は避けるべきです。

先ほど、配列を走査するさまざまな方法について説明しましたが、配列内の空の位置の処理方法はそれぞれ異なります。

ForEach、reduce、map は走査時に空白を無視しますが、for...of は空白を無視せず、未定義として扱います。

4.2.1 for...ofは空白の判断を追加する

したがって、for...of で配列を走査する myFlat メソッドを改善する必要があります。

関数 myFlat(arr, 深さ = 1) {
  深さ<= 0の場合{
    arr を返します。
  }
  res = [] とします。
  for (arrのconst項目) {
    Array.isArray(item) の場合 {
      res = res.concat(myFlat(item, depth - 1));
    } それ以外 {
      // 配列が空かどうかを判断します。item !== undefined && res.push(item);
    }
  }
  res を返します。
}

4.2.2 forEach と map メソッドのトラバーサル

もちろん、forEach メソッドや map メソッドを使用して配列を走査することもできるので、手動で判断する必要はありません。

しかし、考慮すべき特別なケースがあります。つまり、depth <= 0 の場合、フィルター メソッドを使用して配列の空き領域を削除します。

// 各
関数 myFlat(arr, 深さ = 1) {
  深さ<= 0の場合{
    arr.filter(item => item !== undefined) を返します。
  }
  res = [] とします。
  arr.forEach((アイテム) => {
    Array.isArray(item) の場合 {
      res = res.concat(myFlat(item, depth - 1));
    } それ以外 {
      res.push(アイテム);
    }
  });
  res を返します。
}
 
// マップ
関数 myFlat(arr, 深さ = 1) {
  深さ<= 0の場合{
    arr.filter(item => item !== undefined) を返します。
  }
  res = [] とします。
  arr.map((アイテム) => {
    Array.isArray(item) の場合 {
      res = res.concat(myFlat(item, depth - 1));
    } それ以外 {
      res.push(アイテム);
    }
  });
  res を返します。
}

4.2.3 削減法

その中でもreduceメソッドは最も簡潔であり、面接でよくテストされるメソッドの1つでもあります。

関数 myFlat(arr, 深さ = 1) {
  深さ > 0 を返す
    ? arr.reduce() 関数
        (前、現在) =>
          pre.concat(Array.isArray(cur) ? myFlat(cur, depth - 1) : cur),
        []
      )
    : arr.filter((item) => item !== 未定義);
}

5 その他

5.1 スタック

理論的には、再帰メソッドは通常、スタックを使用して非再帰メソッドに変換できます。

関数 myFlat(arr) {
  res = [] とします。
  const スタック = [].concat(arr);
  スタックの長さが0より大きい場合
    定数項目 = stack.pop();
    Array.isArray(item) の場合 {
      // スプレッド演算子を使用して 1 つのレイヤーを展開します。stack.push(...item);
    } それ以外 {
      項目 !== 未定義 && res.unshift(item);
    }
  }
  res を返します。
}

ただし、この方法では拡張の深さを指定できず、1 次元配列にのみ完全に拡張できます。

5.2 改善点

スタックが拡張深度を指定できないという欠点を改善するためのコードは次のようになります。

関数 myFlat(arr, 深さ = 1) {
  深さ<= 0の場合{
    arr.filter((item) => item !== undefined) を返します。
  }
  解決しましょう;
  キューを [].concat(arr);
  (深さ > 0)の間{
    解像度 = [];
    キュー.forEach((アイテム) => {
      Array.isArray(item) の場合 {
        // スプレッド演算子を使用して配列を展開する前に、フィルター メソッドを使用して空のスペースを削除することに注意してください res.push(...item.filter((e) => e !== undefined));
      } それ以外 {
        res.push(アイテム);
      }
    });
    深さ - ;
    キュー = res;
  }
  res を返します。
}

要約する

JavaScript 面接で配列のフラット化を実装する方法についての記事はこれで終わりです。JS で配列のフラット化を実装する方法の詳細については、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JS配列重複排除の詳細
  • JavaScript配列の重複排除のいくつかの方法についての詳細な説明
  • JavaScript 配列重複排除問題の詳細な研究
  • JavaScript 配列重複排除ソリューション
  • jsは配列の平坦化を実装します
  • JavaScript データのフラット化の詳細な説明
  • 配列をフラット化する 5 つの JavaScript の方法
  • JavaScript 配列の重複排除とフラット化関数の紹介

<<:  Dockerコンテナにホストディレクトリへの書き込み権限がない場合の解決策

>>:  チェックボックスとラジオボタンの配置を実装する方法

推薦する

HTML 描画ユーザー登録ページ

この記事では、HTML描画ユーザー登録ページの具体的な実装コードを参考までに共有します。具体的な内容...

MySQL 8.0.21.0 コミュニティ エディションのインストール チュートリアル (詳細な図解)

1. MySQLをダウンロードするMySQL 公式 Web サイトにログインし、MSI インストー...

MySql で、存在しない場合は挿入し、存在する場合は更新する方法

まとめシナリオによっては、レコードがない場合は挿入し、レコードがある場合は更新するという要件がある場...

CSS が複数のクラスに一致する方法のサンプルコード

CSSは複数のクラスにマッチする次の HTML タグ li、クラスはオープン スタイルです。私の要件...

画像の色を変更するための純粋なCSS

画像の色を変更するための CSS テクニックは非常にシンプルです。具体的なコードは次のとおりです。ヒ...

vue.js ベースの QQ チャット ルーム

目次導入効果のデモンストレーションは次のとおりです。 MChat コンポーネントのレンダリング: I...

JavaScriptにおけるこのポインティング問題の詳細な説明

序文JS の this ポインターは、初心者にとって常に頭痛の種でした。今日は、これが地面に落ちたと...

画像をMySQLデータベースに保存し、フロントエンドページに表示するための実装コード

目次1. まず、pycharmを使用してDjangoプロジェクトを作成し、関連する環境を設定します。...

Maven+Tomcat 基本イメージを構築する Docker の実装

序文Javaプログラミングでは、ほとんどのアプリケーションはMavenに基づいて構築されており、配信...

Windows で MySQL 5.6 を 5.7 にアップグレードする方法

前面に書かれたMySQL をアップグレードする方法には、インプレース アップグレードと論理アップグレ...

TLS暗号化通信を使用してDockerにリモート接続する詳細な例

デフォルトでは、Docker はネットワーク化されていない UNIX ソケット上で実行されます。オプ...

ReactのuseEffectクロージャの落とし穴についての簡単な説明

問題コードuseEffectによって発生したクロージャの問題コードを見てみましょう 定数 btn =...

異なるブラウザ間で互換性のあるテキスト配置を実現する CSS

フォームのフロントエンド レイアウトでは、テキスト ボックスのプロンプト テキストを両端に揃える必要...

Docker を使用して Microsoft Sql Server を展開するための詳細な手順

目次1 背景2 コンテナを作成する3 SAパスワードを変更する4 mssql のリンク5. コンテナ...

Alibaba Cloud ホストが IP を使用して Web サイトにアクセスできない問題の解決策 (セキュリティ グループ ルールを構成することで解決)

Alibaba Cloud ホストを購入したばかりで、その速度を試すのが待ちきれませんでした。しか...