JavaScript配列を作成するためのハック

JavaScriptで配列を作成および複製するための洞察に満ちたヒント。

UnsplashのMarkus Spiskeによるオリジナル写真

すべてのプログラミング言語の非常に重要な側面は、その言語で使用可能なデータ型と構造です。ほとんどのプログラミング言語は、複雑なデータを表現および操作するためのデータ型を提供します。 PythonやRubyなどの言語を使用したことがある場合は、リスト、セット、タプル、ハッシュ、辞書などのデータ型を見たことがあるはずです。

JavaScriptでは、それほど多くの複雑なデータ型はありません。単純に配列とオブジェクトがあります。ただし、ES6では、シンボル、セット、マップなどのいくつかのデータ型と構造が言語に追加されました。

JavaScriptの配列は、インデックスとして長さプロパティと整数プロパティを持つ高レベルのリストのようなオブジェクトです。

この記事では、新しいJavaScript配列を作成したり、既存の配列を複製したりするためのいくつかのハックを紹介します。

配列の作成:配列コンストラクター

配列を作成する最も一般的な方法は、配列リテラル構文を使用することです。これは非常に簡単です。ただし、配列を動的に作成する場合、配列リテラル構文が常に最良の方法であるとは限りません。別の方法は、Arrayコンストラクターを使用することです。

以下は、Arrayコンストラクターの使用を示す簡単なコードスニペットです。

前のスニペットから、Arrayコンストラクターは、受け取った引数に応じて異なる方法で配列を作成することがわかります。

新しい配列:定義された長さ

与えられた長さの新しい配列を作成するときに何が起こるかをより詳しく見てみましょう。コンストラクタは、キーを設定せずに、配列のlengthプロパティを指定の長さに設定するだけです。

上記のスニペットから、配列内の各キーが未定義の値に設定されていると思われるかもしれません。しかし、現実には、これらのキーは決して設定されていません(存在しません)。

次の図はそれをより明確にします:

これにより、map()、filter()、reduce()などの配列反復メソッドを使用して配列を操作しようとしても無駄になります。配列の各インデックスに値5を入力するとします。以下を試みます。

インデックスプロパティが配列に存在せず、長さプロパティのみが存在するため、map()がここでは機能しなかったことがわかります。

この問題を修正するさまざまな方法を見てみましょう。

1. Array.prototype.fill()を使用する

fill()メソッドは、開始インデックスから終了インデックスまでの配列のすべての要素を静的な値で埋めます。終了インデックスは含まれません。 fill()の詳細については、こちらをご覧ください。

fill()は、ES6をサポートするブラウザーでのみ機能することに注意してください。

以下に簡単な図を示します。

ここでは、作成した配列のすべての要素を5で埋めることができました。fill()メソッドを使用して、配列のさまざまなインデックスに静的な値を設定できます。

2. Array.from()を使用する

Array.from()メソッドは、配列のようなオブジェクトまたは反復可能なオブジェクトから、浅くコピーされた新しいArrayインスタンスを作成します。 Array.from()の詳細については、こちらをご覧ください。

Array.from()はES6をサポートするブラウザーでのみ機能することに注意してください。

以下に簡単な図を示します。

ここで、Array.from()を使用して配列の各要素に設定された真の未定義値があります。これは、インデックスプロパティが存在するため、先に進み、配列で.map()や.filter()などのメソッドを使用できるようになったことを意味します。

Array.from()について注目に値するもう1つのことは、マップ関数である2番目の引数を取ることができることです。配列のすべての要素で呼び出されます。これにより、Array.from()の後に.map()を呼び出す必要がなくなります。

以下に簡単な例を示します。

3.スプレッド演算子の使用

ES6で追加されたスプレッド演算子(...)を使用して、配列の要素を広げ、欠落している要素を未定義の値に設定できます。これは、配列のみを唯一の引数としてArray.from()を呼び出すだけの場合と同じ結果を生成します。

スプレッド演算子を使用する簡単な例を示します。

インデックスプロパティが存在するため、先に進み、配列に対して.map()や.filter()などのメソッドを使用できます。

Array.of()を使用する

Arrayコンストラクターまたは関数を使用して新しい配列を作成するときに見たように、Array.of()は非常に似た動作をします。実際、Array.of()とArrayの唯一の違いは、渡された単一の整数引数の処理方法です。

Array.of(5)は、1つの要素5と長さプロパティ1で新しい配列を作成しますが、Array(5)は5つの空のスロットと5つの長さプロパティで新しい空の配列を作成します。

var array1 = Array.of(5); // [5]
var array2 = Array(5); // Array(5){length:5}

この大きな違いに加えて、Array.of()はArrayコンストラクターと同じように動作します。 Array.of()の詳細については、こちらをご覧ください。

Array.of()はES6をサポートするブラウザーでのみ機能することに注意してください。

配列への変換:配列ライクとイテラブル

JavaScript関数を十分長く書いている場合は、引数オブジェクトについて既に知っている必要があります。これは、関数が受け取った引数のリストを保持するためにすべての関数で使用できる配列のようなオブジェクトです。 argumentsオブジェクトは配列のように見えますが、Array.prototypeメソッドにアクセスすることはできません。

ES6より前は、引数オブジェクトを配列に変換しようとすると、通常、次のようなコードスニペットが表示されます。

Array.from()またはスプレッド演算子を使用すると、配列のようなオブジェクトを配列に簡単に変換できます。したがって、これを行う代わりに:

var args = Array.prototype.slice.call(arguments);

次のいずれかを実行できます。

// Array.from()を使用
var args = Array.from(arguments);
// Spread演算子を使用
var args = [... arguments];

これらは、次の図に示すようにイテラブルにも適用されます。

ケーススタディ:範囲関数

先に進む前のケーススタディとして、簡単なrange()関数を作成して、先ほど学習した新しい配列ハックを実装します。この関数には次のシグネチャがあります。

範囲(開始:番号、終了:番号、ステップ:番号)=>配列<番号>

コードスニペットは次のとおりです。

このコードスニペットでは、Array.from()を使用して、動的な長さの新しい範囲配列を作成し、マッピング関数を提供することにより、連続してインクリメントされる数値を設定します。

上記のコードスニペットは、ポリフィルを使用する場合を除き、ES6がサポートされていないブラウザーでは機能しないことに注意してください。

上記のコードスニペットで定義されているrange()関数を呼び出した結果を次に示します。

Codepenで次のペンを実行すると、ライブコードデモを取得できます。

配列のクローン作成:課題

JavaScriptでは、配列とオブジェクトは参照型です。つまり、変数に配列またはオブジェクトが割り当てられると、変数に割り当てられるのは、配列またはオブジェクトが保存されたメモリ内の場所への参照です。

JavaScriptの他のすべてのオブジェクトと同様に、配列は参照型です。つまり、配列は値ではなく参照によってコピーされます。

この方法で参照型を保存すると、次の結果が生じます。

1.類似の配列は等しくありません。

ここでは、array1とarray2には一見同じ仕様の配列が含まれていますが、等しくないことがわかります。これは、各配列への参照がメモリ内の異なる場所を指しているためです。

2.配列は、値ではなく参照によってコピーされます。

ここでは、array1をarray2にコピーしようとしますが、基本的にはarray2がarray1が指すメモリ内の同じ場所を指すようにします。したがって、array1とarray2の両方がメモリ内の同じ場所を指し、等しくなります。

これが意味するのは、最後の項目を削除してarray2を変更すると、array1の最後の項目も削除されるということです。これは、実際にはメモリに格納されている配列に変更が加えられたのに対し、array1とarray2は配列が格納されているメモリ内の同じ場所へのポインタにすぎないためです。

配列のクローニング:ハック

1. Array.prototype.slice()を使用する

slice()メソッドは、配列を変更せずに配列の一部の浅いコピーを作成します。 slice()の詳細については、こちらをご覧ください。

秘Theは、with0を唯一の引数として、または引数なしでslice()を呼び出すことです。

//引数としてのみOを使用
array.slice(0);
//引数なし
array.slice();

これは、slice()を使用して配列を複製する簡単な例です。

ここで、array2は同じアイテムと長さを持つarray1のクローンであることがわかります。ただし、それらはメモリ内の異なる場所を指しているため、結果として等しくありません。また、最後の項目を削除してarray2を変更しても、array1は変更されないままであることに注意してください。

2. Array.prototype.concat()を使用する

concat()メソッドは、2つ以上の配列をマージするために使用され、新しい配列を生成しますが、元の配列は変更されません。 concat()の詳細については、こちらをご覧ください。

トリックは、concat()を引数として空のarray([])を使用して、または引数なしで呼び出すことです。

//空の配列で
array.concat([]);
//引数なし
array.concat();

concat()を使用して配列を複製することは、slice()を使用することに非常に似ています。 concat()を使用して配列を複製する簡単な例を次に示します。

3. Array.from()を使用する

前に見たように、Array.from()を使用して、元の配列の浅いコピーである新しい配列を作成できます。以下に簡単な図を示します。

4.配列の破壊の使用

ES6では、破壊、スプレッド演算子、矢印関数など、ツールボックスにさらに強力なツールがいくつかあります。破壊は、配列やオブジェクトなどの複雑なタイプからデータを抽出するための非常に強力なツールです。

この記事から破壊の詳細を学ぶことができます。

秘Theは、restパラメーターと呼ばれる手法を使用することです。この手法には、次のスニペットに示すように、配列の破壊とスプレッド演算子の組み合わせが含まれます。

let [... arrayClone] = originalArray;

上記のスニペットは、originalArrayのクローンであるarrayCloneという名前の変数を作成します。以下は、配列の破壊を使用して配列を複製する簡単な図です。

クローニング:浅い対深い

これまでに検討したすべてのアレイクローン作成技術は、アレイの浅いコピーを作成します。配列にプリミティブ値のみが含まれる場合、これは問題になりません。ただし、配列にネストされたオブジェクト参照が含まれている場合、それらの参照は、配列が複製されてもそのまま残ります。

これの非常に簡単なデモンストレーションはここにあります:

array1のネストされた配列を変更すると、array2のネストされた配列も変更され、その逆も同様です。

この問題の解決策は、アレイのディープコピーを作成することです。これを行うには、いくつかの方法があります。

1. JSONテクニック

配列のディープコピーを作成する最も簡単な方法は、JSON.stringify()とJSON.parse()の組み合わせを使用することです。

JSON.stringify()はJavaScript値を有効なJSON文字列に変換し、JSON.parse()はJSON文字列を対応するJavaScript値またはオブジェクトに変換します。

以下に簡単な例を示します。

JSON技術には、特に文字列、数値、ブール値以外の値が関係する場合にいくつかの欠点があります。

JSONテクニックのこれらの欠陥は、主にJSON.stringify()メソッドが値をJSONストリングに変換する方法に起因します。

ネストされた関数を含む値をJSON.stringify()しようとする際のこの欠陥の簡単なデモを次に示します。

2.ディープコピーヘルパー

JSONテクニックの実行可能な代替手段は、参照タイプが配列であろうとオブジェクトであろうと、参照タイプを複製するための独自のディープコピーヘルパー関数を実装することです。

以下は、deepCloneと呼ばれる非常にシンプルで最小限のディープコピー関数です。

JavaScriptライブラリですぐにわかるように、これはディープコピー関数のベストではありませんが、かなりの程度までディープコピーを実行します。

3. JavaScriptライブラリの使用

先ほど定義したディープコピーヘルパー関数は、複雑なオブジェクトまたは配列内にネストされる可能性のあるすべての種類のJavaScriptデータを複製するのに十分なほど堅牢ではありません。

LodashやjQueryなどのJavaScriptライブラリは、さまざまな種類のJavaScriptデータをサポートする、より堅牢なディープコピーユーティリティ関数を提供します。

Lodashライブラリの_.cloneDeep()を使用する例を次に示します。

同じ例ですが、jQueryライブラリの$ .extend()を使用しています。

結論

この記事では、新しい配列を動的に作成し、既存の配列を複製するためのいくつかの手法を検討することができました。これには、配列のようなオブジェクトとイテラブルの配列への変換が含まれます。

また、ES6で導入されたいくつかの新機能と機能強化により、アレイで特定の操作を効果的に実行できることも確認しました。

配列のクローン作成と展開には、デストラクタリングやスプレッド演算子などの機能を使用しました。この記事から破壊の詳細を学ぶことができます。

クラップ&フォロー

この記事を洞察力に富んでいると思うのであれば、気にしないなら拍手をしてください。

また、Medium(Glad Chinda)で私をフォローして、役に立つ洞察に満ちた記事を見つけることもできます。 Twitter(@gladchinda)で私をフォローすることもできます。

ハッピーハッキング…