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コンテナにホストディレクトリへの書き込み権限がない場合の解決策

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

推薦する

1 つの記事で JSON (JavaScript Object Notation) を理解する

目次JSONが登場JSON構造JSONオブジェクトJson オブジェクトと JavaScript オ...

WeChatアプレットが連携メニューを実現

最近はコース設計を実現するために、フロントエンドも少しやっています。今日はいくつかの機能を実現するた...

Ubuntu Linuxシステムをインストールするときにハードディスクをパーティション分割する最も合理的な方法の詳細な説明

Windows または Linux オペレーティング システムをインストールするかどうかに関係なく、...

Vue の状態管理: Vuex の代わりに Pinia を使用する

目次1. ピニアとは何ですか? 2. Piniaは使いやすい3. ユーザーエクスペリエンス1. ピニ...

Linux システムによって報告される tcp_mark_head_lost エラーの処理方法

問題の説明最近、ホストから次のカーネル情報が報告されました。 7月8日 10:47:42 cztes...

MySQL でテーブルスペースの断片化を解消する詳細な例

MySQL でテーブルスペースの断片化を解消する詳細な例断片化の原因(1)テーブルのストレージは断片...

クラウドサーバーを購入し、Alibaba Cloud に Pagoda Panel をインストールする手順

アリババクラウドがサーバーを購入クラウドサーバーを購入し、サーバーバージョンとしてcentos 7....

MySQLの起動と接続方法の例分析

目次mysqldの起動方法方法 1: mysqld方法 2: mysqld_safe方法3: mys...

CentOS 7 ブートカーネルの切り替えとブートモードの切り替えの説明

Centos7 スイッチブートカーネル注: 必要に応じて、最初にyum update -yを実行して...

Linux コマンドラインターミナルで画面を分割するための 2 つのツール

ターミナル分割画面ツールは2つあります: screen と tmux 1. 画面分割を使用する(上下...

JavaScript Sandboxについての簡単な説明

序文:サンドボックスといえば、私たちの頭には反射的に上の写真が思い浮かび、すぐに興味がわいてくるかも...

HTML ベース URL タグ

その機能はグローバル スタイルを設定することです。その後の相対パスはこれに基づきます: <im...

int(3)とint(10)の値の範囲はmysqlで同じですか?

目次質問:答え:現実:知識ポイント結論は:要約する質問: MySQLフィールド、unsigned i...

Vue3.0 手書き拡大鏡効果

達成される効果は、固定ズームが 2 倍になり、マウスが左側の画像領域に入るとマスク レイヤーが表示さ...

CSS を使用して適応型の幅と高さを持つ 16:9 の長方形を実装する例

先ほど、適応幅と高さが1:1の正方形を作成する方法について説明しました。 https://www.j...