配列のメソッド
《 初回公開:2023/10/19 , 最終更新:未 》
【 目次 】
配列のメソッドにはどんなものが
配列のメソッドは数が多くてとらえにくい。
自分なりに配列のメソッドについて分類分けしてみた。
クラスメソッド
- Array.isArray
配列かどうかの判定 - Array.of
与えられた引数を要素とする新しい配列を作成 - Array.from
[反復可能なオブジェクト]またはArray-likesオブジェクト(文字列、Set、Mapなど)から、浅くコピーされた新しい配列を生成
要素の反復処理 - 反復処理メソッド
Arrayクラスのメソッドには反復処理メソッドと呼ばれる配列の要素を先頭から順に走査して効果的に反復処理するためのさまざまなメソッドがサポートされている。
各メソッドはコールバック関数を受け取り、その関数が要素ごとに実行される。
これらのメソッドは、簡潔で効率的で読みやすいコードを記述するのに有効。
反復処理メソッドには以下のものがある。
- forEach - 配列の要素を先頭から順番にひとつづつ取り出し処理する
- map - 配列の各要素に対してコールバック関数を実行し、その結果から新しい配列を作成して返す
- flatMap - map動作により生成された結果の配列をフラット化して返す
- reduce - 配列の各要素を先頭(左)から順に累積演算をおこないその結果返す
- reduceRight - 配列の各要素を最後(右)から順に累積演算をおこないその結果返す
- every - 配列のすべての要素が与えられた条件を満たすかどうかを判定する
- some - 要素のどれか一つでも条件を満たすかどうかを判定する
- filter - 配列の要素をフイルタリングして条件にあった要素のみの配列を返す。
- find,findIndex,findLast,findLastIndex - 指定した条件を満たす最初(または最後)の要素を検索
以下は反復処理メソッドの1つであるforEachを使って配列の要素をひとつひとつコンソールに出力する例。
const array = [1, 2, 3];
array.forEach((element) => {
console.log(element);
});
// 出力:
// 1
// 2
// 3
要素の操作(変更, 追加や削除、要素の順番の入れ替えなど)
- 指定されたインデックス位置の配列の要素の値を取得,設定する - at, with
- 配列内の要素を同じ配列内の別の要素にコピー - copyWithin
- 複数の配列を連結して新しい配列を生成 - concat
- 指定した範囲の配列の要素を指定した値で上書き - fill
- 配列内の要素の順序を逆順にする - reverse, toReversed
- 配列のネストを平坦化 - flat
- 配列の先頭や末尾に要素を追加,削除 - push, pop, shift, unshift
- 配列のソート - sort, toSorted
- 配列の要素の削除と追加を同時おこなう - splice, toSpliced
- 配列の一部を取り出す - slice
その他
- 配列が指定した値を含んでいるかどうかを判定する - includes
- 配列内の要素の位置(インデックスの値)を返すメソッド - indexOf, lastIndexOf
- 配列のキーや要素を含むイテラブルなオブジェクトを返す - keys, values, entries
空のスロットに対する配列のメソッドの扱い
メソッドによって空のスロットに対する扱いが異なる。
配列のメソッドには空のスロットをスキップするメソッドとそうでないメソッドが存在する。
そして、古いメソッドは空のスロットをスキップするが、新しいメソッドはそれを undefined として扱う傾向がある。
また、メソッドによっては空のスロットをundefined値に置き換えたりする可能性もあり注意が必要。
コピーメソッドと変更メソッド(非破壊的メソッドと破壊的メソッド)
Arrayクラスのメソッドは、
コピーメソッドと呼ばれる元の配列を変更しないで元の配列を基に新しい配列を生成して返すメソッドと、
変更メソッドと呼ばれる元の配列自体を変更・更新するメソッドとがある。
コピーメソッドは元のデータが変更される心配がなく非破壊的メソッドとも呼ばれる。
コピー動作はシャローコピーとなる。
変更メソッドは元の配列を変更するので破壊的メソッドとも呼ばれる。
メソッドチェイン
配列のメソッドの中には配列を返すメソッドが多くあり、これらのメソッドはメソッドどうしを「.(ドット)」で連結して繋げて処理する事ができる。
いわゆるメソッドチェインが可能。
非破壊的メソッドは新しい配列を返すのでこれをメソッドチェインでつなぐ事で、次々と効果的に配列の変換、操作、フィルタリングすることができる。
以下のコードはmapメソッドとfilterメソッドを組み合わせて、元の配列の要素を3倍した値のなかで結果が奇数である要素を抜き出す。
元の配列は変化していない。
const array = [2, 3, 5, 8];
const result = array.map((element) => element *3).filter(value => value % 2);
console.log(result, array); // [9, 15] [2, 3, 5, 8]
一方、破壊的メソッドの中にも変更された元の配列の参照を返すメソッドが存在し、メソッドチェインでつなぐ事が可能なものがある。
以下のコードはfillメソッドとreverseメソッドを組み合わせてインデックスが1の要素をxに変更したうえ配列内の要素の順序を逆に反転させる。
fillメソッドもreverseメソッドも破壊的メソッドで元の配列も変更される。
const array = ["a", "b", "c"];
const result = array.fill("x", 1, 2).reverse();
console.log(result, array); // ['c', 'x', 'a'] ['c', 'x', 'a']
さらに破壊的メソッドと非破壊的メソッドの組み合わせを使用して、より複雑な処理を行うことができる。
コピーメソッドはシャローコピー
コピーメソッドはシャローコピーである事に注意。
const array = ["a", { name: "愚鈍人", age: 99 }];
const array2 = ["x", "y"];
var result = array.concat(array2);
// resultは2つの配列が連結された新しい配列
console.log(JSON.stringify(result)); // ["a",{"name":"愚鈍人","age":99},"x","y"]
// 新しい配列の要素のプロパティを変更すると
result[1].name = "xxx";
// シャローコピーなので元の配列の要素のプロパティも変更される
console.log(JSON.stringify(array)); // ["a",{"name":"xxx","age":99}]
破壊的メソッドと非破壊的メソッドの対比
破壊的メソッドにはそれと同じ機能を持つ非破壊的メソッドが存在するものもある。
Arrayクラスのインスタンスメソッドについて破壊的メソッドと非破壊的メソッドについて表にしてまとめてみると。
機能 | 非破壊的メソッド | 破壊的メソッド |
---|---|---|
反復処理メソッド | forEach, map, flatMap, reduce, reduceRight, every, some, filter, find, findIndex, findLast, findLastIndex | コールバック関数内で元の配列の要素を変更する事は可能 |
指定されたインデックス位置の配列の要素の値を取得 | at | - |
指定されたインデックス位置の配列の要素の値を設定 | with | - |
配列内の要素を同じ配列内の別の要素にコピー | - | copyWithin |
複数の配列を連結して新しい配列を生成 | concat | - |
指定した範囲の配列の要素を指定した値で上書き | - | fill |
配列内の要素の順序を逆順にする | toReversed | reverse |
配列のネストを平坦化 | flat | - |
配列の先頭や末尾に要素を追加,削除 | - | push, pop, shift, unshift |
配列のソート | toSorted | sort |
配列の要素の削除と追加を同時おこなう | toSpliced | splice |
配列の一部を取り出す | slice | - |
配列が指定した値を含んでいるかどうかを判定する | includes | - |
配列内の要素の位置(インデックスの値)を返すメソッド | indexOf, lastIndexOf | - |
配列のキーや要素を含むイテラブルなオブジェクトを返す | keys, values, entries | - |
array-like objectと配列のメソッド
array-like object - 配列のようなオブジェクト
JavaScriptにはarray-like objectという配列のようなオブジェクトがある。
ChartGPTに「array-like object」について説明してもらうと
「array-like object(配列のようなオブジェクト)」とは、配列と同じようなインデックス付きのプロパティを持つオブジェクトを指します。通常のJavaScriptの配列とは異なり、配列のように .length プロパティを持つことが特徴です。例えば、文字列やNodeListオブジェクト、argumentsオブジェクトなどが「array-like object」の代表的な例です。ただし、配列とは異なり、Array オブジェクトが持つような配列メソッドや、配列専用の機能は使用できないため、通常の配列として扱う場合には変換が必要です。Array.from() メソッドやスプレッド構文を使うことで、array-like object を配列に変換することができます。
JavaScriptには、配列に似たオブジェクト(array-like object)がいくつかあります。以下に一部を示します。
- 文字列(string)
- argumentsオブジェクト(関数内で利用できる特別なオブジェクト)
- NodeListオブジェクト(HTMLの要素を取得するAPIで得られるオブジェクト)
- HTMLCollectionオブジェクト(DOMの要素を取得するAPIで得られるオブジェクト)
- TypedArrayオブジェクト(バイナリデータを扱うためのオブジェクト)
- DataViewオブジェクト(バイナリデータを扱うためのオブジェクト)
これらのオブジェクトは、通常の配列のようにインデックスを用いた要素の取得・設定が可能ですが、配列のメソッドは一部しか持っていません。また、配列とは異なるオブジェクトであるため、配列と同じように扱うことができない場合もあります。
「array-like object」は配列のようなオブジェクトという事で配列のようにインデックスとlengthプロパティを持っているがArrayクラスのインスタンスではないオブジェクトを指す。
「array-like object」はMDNのドキュメントでは配列ライクとか配列風オブジェクトと呼ばれている。
「array-like object」は配列のように扱う事ができて。
const arrayLike = {
0: "a",
1: "b",
length: 2,
};
for (let i = 0; i < arrayLike.length; i++) {
console.log(arrayLike[i]);
}
// a
// b
しかし「array-like object」だけでは配列のようにfor..of構文を使って反復処理をおこなう事ができなくて、
「array-like object」とは別に配列のようにふるまうオブジェクトに反復可能オブジェクトがあって、反復可能オブジェクトを実装するにはSymbol.iterator メソッドを追加して反復可能プロトコルを実装する事でfor..of構文を使って反復処理をおこなう事がでる。
「array-like object」に反復可能プロトコルを実装
const arrayLikeIterable = {
0: "a",
1: "b",
length: 2,
*[Symbol.iterator]() {
for (let i = 0; i < this.length; i++) {
yield this[i];
}
}
}
for (let c of arrayLikeIterable) {
console.log(c);
}
// a
// b
文字列は「array-like object」でありかつ反復可能なオブジェクトとして実装されている。
const s = "abc";
for (let i = 0; i < s.length; i++) {
console.log(s[i]);
}
for (const c of s) {
console.log(c);
}
// a
// b
// c
Array-likeオブジェクトか配列なのかを判別するにはArray.isArrayメソッドを利用できる。
また、Array-likeオブジェクトはArray.fromを使って簡単に配列に変換する事もできる。
汎用的な配列メソッド
配列メソッドは常に汎用的であり、配列オブジェクトの内部データにはアクセスしません。配列の要素には length プロパティを通してアクセスし、添字の要素にはアクセスするだけです。つまり、配列風のオブジェクトに対しても呼び出すことができます。
配列のメソッドは汎用的につくられていて、applyメソッドやcallメソッドを利用することで「array-like object」に対しても有効に動作する。
const arrayLike = {
0: "a",
1: "b",
length: 2,
};
console.log(Array.prototype.join.call(arrayLike, "+")); // 'a+b'
//arrayLike.forEach(element => console.log(element)); <= これはエラーだが
// callメソッドを利用して
Array.prototype.forEach.call(arrayLike, element => console.log(element));
// またはapplyメソッドを利用して
Array.prototype.forEach.apply(arrayLike, [element => console.log(element)]);
配列のコピー
破壊的メソッドを使うと元の配列が破壊されてしまう事になる。
元の配列が破壊されると困る場合には、破壊的メソッドを実行する前に配列のコピーを取っておく必要がある。
ここでは、配列のディープコピーでは無くシャローコピーの方法について検討してみる。
配列のコピーにはいろいろとあって。
for文等のループ処理を使う
まず思いつくのが、forやwhile文を使っってループ処理をおこなう事。
let copy_array = new Array(array.length);
for (let i = 0; i < array.length; i++) {
copy_array[i] = array[i];
}
スプレッド構文を使う
一番簡単でスマートなのがスプレッド構文を使う事
const array = ["a", "b", "c"];
let copy_array = [...array];
Array.fromメソッドを使う
Array.fromメソッドは反復可能なオブジェクトまたはArray-likesオブジェクトから配列を生成するメソッドであるが、Array.fromメソッドの引数に配列を指定すると元の配列のコピーが生成される。
let copy_array=Array.from(array)
反復処理メソッドを使う
反復処理メソッドを利用する事で配列のコピーを生成できる。
以下はmapメソッドを使った例。
let copy_array = array.map((x) => x);
concatメソッドを使う
concatメソッドは配列どうしを結合して新しい配列を生成するためのメソッドであるが、引数を省略する事により配列のコピーを生成できる。
let copy_array = array.concat();
sliceメソッドを使う
sliceメソッドは要素の範囲を指定して配列の部分配列を取得するメソッド、範囲を要素の全範囲に指定する事により配列のコピーを生成できる。
let copy_array = array.slice(0, array.length);
ディープコピーについては以下を参照。
配列のシャローコピーと空きスロットの要素
上記の例ではいずれの場合も、配列のシャローコピーをおこなうと空きスロットの要素はundefinedに変換されてしまうので注意。