目的
- MDNに飛ぶための索引として
- 破壊非破壊かどうかの区別
- 区別して使い分けないと、意図しない動作によりバグの元に
- 破壊的なメソッドは、そのメソッドの実行結果として、元の配列に変更が加わる(元の配列の参照に変更が加わる)
- 非破壊的なメソッドは、あるメソッドの実行結果として、元の配列に変更が加わらない(元の配列のコピーに変更が加わる)
- 区別して使い分けないと、無駄なコピーを作り、メモリ消費量や速度に悪影響が生じる
- 区別して使い分けないと、意図しない動作によりバグの元に
環境
- macOS Monterey 12.0.1
- VSCode
- node.js v18.6.0
- npm 8.13.2
- Typescript 4.6.4
ユースケース別
配列データ例(TODOリスト)
const todoList = [
{ id: 1, title: "洗濯", isCompleted: false },
{ id: 2, title: "プログラミング学習", isCompleted: true },
{ id: 3, title: "掃除", isCompleted: false },
{ id: 4, title: "買い物", isCompleted: true },
];
元の配列をベースにして、別の配列にしたい
map
- 与えた関数を配列の各要素に対して適用し、その結果を返り値として返す
- 非破壊
- 具体例
- TODOのIDだけの配列にしたい
const todoIds = todoList.map((todo) => todo.id) // [1, 2, 3, 4]
- 繰り返し系のメソッドとしては、同じく非破壊のforEachもある
- forEachは戻り値がなく、割となんでもできるが故に、一見何の処理をしているのかわかりにくくなりがち
- 基本的にはmapやfilterなどのメソッド名から意味を推測しやすいものを使用し、どうしても実現できない場合にforEachを使用する(くらいの認識)
配列内の特定の要素の位置を調べたい
findIndex
- 引数に与えたテスト関数に合格する最初の要素のインデックス番号を取得
- 非破壊
- IDが3のtodoのインデックスを取得したい
const index = todoList.findIndex((todo) => todo.id === 3) // 2
配列内のある条件に一致する要素を取得したい
find
- 単一の要素を取得
- 非破壊
- IDが4のtodoを取得したい
const todo = todoList.find((todo) => todo.id === 4) // {id: 4, title: '買い物', isCompleted: true}
filter
- 単一または複数要素を配列で取得
- 非破壊
- 未完了のtodoを全て取得したい
/*
[
0: {id: 1, title: '洗濯', isCompleted: false},
1: {id: 3, title: '掃除', isCompleted: false},
]
*/
Tips: 配列同士の差分を抽出する方法
- indexOfは、要素が見つからない場合は-1を返すので、片方の配列にしか存在しない要素の配列をfilterを使用して新たに生成できる
const list1 = [1, 2, 3, 4, 5, 6];
const list2 = [1, 2, 3, 4];
list3 = list1.filter(num => list2.indexOf(num) === -1) // [5, 6]
配列のすべての要素の値を合計した値を取得する
reduce
- 非破壊
- 配列の数値の合計を計算する
const result = [1, 2, 3, 4, 5, 6, 10].reduce(
(prev, current) => prev + current
); // 31
配列に特定の要素が含まれているかどうかをチェックする
includes
- 非破壊
[1, 2, 3, 4, 5, 6, 10].includes(6) // true
配列から別の配列のコピーを作成する
スプレッド構文
- 非破壊
console.log(todoList === todoList); // true
console.log(todoList === [...todoList]); // false
slice
- 非破壊
console.log(todoList === todoList); // true
console.log(todoList === todoList.slice(0, 4)); // false
配列の末尾に1つ以上の要素を追加する
push
- 破壊
todoList.push({ id: 5, title: "読書", isCompleted: false });
console.log(todoList);
/*
[
0: {id: 1, title: '洗濯', isCompleted: false},
1: {id: 2, title: 'プログラミング学習', isCompleted: true},
2: {id: 3, title: '掃除', isCompleted: false},
3: {id: 4, title: '買い物', isCompleted: true},
4: {id: 5, title: '読書', isCompleted: false},
]
*/
配列の要素をソートする
sort
- 破壊的なメソッドなので元の配列に変更が加わる点に注意(対象の配列のコピーをスプレッド構文等で生成してそちらに対して行う等する)
- デフォルトでは配列の各要素は、一旦文字列に変換され、UTF-16コード単位の値の並び(昇順)として比較される
- 配列の要素が数値や連想配列等の場合はデフォルトのソート仕様では対応できないので、sortにコールバックを与える必要がある
例1(数値)
- 以下のcallbackの引数であるa,bはそれぞれ配列内の要素を指す
- aの方が大きい場合は「1」を返してaよりbを前に配置、小さい場合は「-1」を返してbよりaを前に配置、等しい場合は「0」を返して並び替えをしない
// BAD
console.log([100, 240, 31, 4, 53, 64, 10, 1].sort()); // [1, 10, 100, 240, 31, 4, 53, 64]
// GOOD
console.log([100, 240, 31, 4, 53, 64, 10, 1].sort(callback)); // [1, 4, 10, 31, 53, 64, 100, 240]
const callback = (a, b) => {
if (a > b) {
return 1;
}
if (a < b) {
return -1;
}
return 0;
};
例2(連想配列)
- 連想配列の場合も、sortの引数に任意のコールバックを渡す必要がある
const callback = (a, b) => (a.priority < b.priority ? -1 : 1); // priorityの昇順ソート
console.log(todoList.sort(callback));
/*
[
0: {id: 3, title: '掃除', isCompleted: false, priority: 1},
1: {id: 2, title: 'プログラミング学習', isCompleted: true, priority: 2},
2: {id: 4, title: '買い物', isCompleted: true, priority: 3},
3: {id: 1, title: '洗濯', isCompleted: false, priority: 4},
]
*/
配列の要素を追加・削除・置換する
splice
- 破壊
- 追加・削除・置換ができて便利なので、使用できる場面が結構多い
- いずれの場合も、インデックス番号を指定する必要があるので、indexOfやfindIndex等と併用することが多かったりする
追加
- 第二引数に0を、第三引数に追加する要素を指定する
//
const index = todoList.length;
const newTodo = {
id: 5,
title: "読書",
isCompleted: false,
priority: 5,
};
todoList.splice(index, 0, newTodo);
console.log(todoList);
/*
[
0: {id: 1, title: '洗濯', isCompleted: false, priority: 4},
1: {id: 2, title: 'プログラミング学習', isCompleted: true, priority: 2},
2: {id: 3, title: '掃除', isCompleted: false, priority: 1},
3: {id: 4, title: '買い物', isCompleted: true, priority: 3},
4: {id: 5, title: '読書', isCompleted: false, priority: 5},
]
*/
削除
- 第二引数に1を指定し、第三引数には何も指定しない
// IDが3のTODOを削除する
const index = todoList.findIndex((todo) => todo.id === 3);
todoList.splice(index, 1);
console.log(todoList);
/*
[
0: {id: 1, title: '洗濯', isCompleted: false, priority: 4}
1: {id: 2, title: 'プログラミング学習', isCompleted: true, priority: 2}
2: {id: 4, title: '買い物', isCompleted: true, priority: 3}
]
*/
- ちなみに以下のようにすると配列の要素をすべて削除できる(インデックス0以降のすべての要素を削除)
todoList.splice(0)
置換
- 第二引数に1を指定し、第三引数に置換したい要素を指定する
// IDが2のTODOを置換する
const index = todoList.findIndex((todo) => todo.id === 2);
const newTodo = {
id: 2,
title: "読書",
isCompleted: false,
priority: 2,
};
todoList.splice(index, 1, newTodo);
console.log(todoList);
/*
[
0: {id: 1, title: '洗濯', isCompleted: false, priority: 4}
1: {id: 2, title: '読書', isCompleted: false, priority: 2}
2: {id: 3, title: '掃除', isCompleted: false, priority: 1}
3: {id: 4, title: '買い物', isCompleted: true, priority: 3}
]
*/
配列内の要素がある条件に該当するかチェックする
every
- 全ての要素が該当するか
- 非破壊
console.log(todoList.every((todo) => todo.isCompleted === true)); // false
some
- 少なくとも1つ以上の要素が該当するか
- 非破壊
console.log(todoList.some((todo) => todo.isCompleted === false)); // true
まとめ
いかがでしたでしょうか。本記事では、Javascript頻用配列メソッドについて具体的なユースケースやコード例を交えながら説明していますので、ぜひ参考にしてみてください。