JavaScript の配列の操作まとめ
JavaScript の配列の基本的な操作方法についてまとめました。
仕事の一環で JavaScript (以下 JS )のコードを書く機会があるのですが、なかなか頻度・量が少ないため、組み込み型の基本的な操作方法を覚えては忘れ、覚えては忘れを繰り返しています。 配列については特に他の言語と似ているところがあるためかなかなか記憶に定着しません。 これではどうも効率が悪いので、 JS の配列の使い方についてまとめておくことにしました。
この記事では原則 主要ブラウザと Node の 2019 年の最新バージョンで使える というのを基本ルールとしています。
目次
- 生成
- 要素数の確認
- 要素の参照
- 要素の代入
- ループ
- 要素の追加
- 要素の取り出し
- 配列を破壊しない要素の取り出し
- 要素の削除
- 要素が含まれるかどうかの判定
- 要素のインデックスの取得
- 条件を満たす要素の取得
- 要素のスライス
- ソート
- マップ
- フィルタ
- 畳み込み
- その他
生成
配列を生成するには記号 []
を使用します。
var a1 = [3, 4, 5];a1 instanceof Array;// => trueArray.isArray(a1);// => true
iterable をもとに配列を生成するには Array.from()
を使用します。
var a1 = Array.from('Hello');// => [ 'H', 'e', 'l', 'l', 'o' ]
要素数の確認
要素数の確認にはプロパティ length
を使用します。
var a1 = [100, 99, 98];a1.length;// => 3
要素の参照
要素の参照には記号 []
と 0 始まりのインデックスを使用します。
var a1 = ['クライミング', 'ラフティング', 'カヤック'];a1[0];// => 'クライミング'a1[1];// => 'ラフティング'a1[2];// => 'カヤック'a1[3];// => undefined
a1[-1]
といった感じでマイナスのインデックスを使うことはできません。
末尾から辿りたいときには length
を使います。
var a1 = ['クライミング', 'ラフティング', 'カヤック'];a1[a1.length - 1];// => 'カヤック'a1[a1.length - 2];// => 'ラフティング'a1[a1.length - 3];// => 'クライミング'
どうしても length
を使わずに末尾から辿れるようにしたいときは、 Proxy
オブジェクトで Array
をラップして使うという小技もあるようです(私は実戦で使ったことはありません)。
var handler = {get(obj, prop) {const index = Number(prop);if (Number.isInteger(index)) {if (index >= 0) {return obj[index];} else {return obj[obj.length + index];}}},set(obj, prop, newval) {const index = Number(prop);if (Number.isInteger(index)) {if (index >= 0) {obj[index] = newval;} else {obj[obj.length + index] = newval;}}}};var a1 = ['クライミング', 'ラフティング', 'カヤック'];var a1plus = new Proxy(a1, handler);a1plus[-1];// => 'カヤック'a1[-2];// => 'ラフティング'a1[-3];// => 'クライミング'a1[-2] = 'サイクリング';a1;// => [ 'クライミング', 'サイクリング', 'カヤック' ]
要素の代入
要素の代入には参照と同じ記号 []
を使用します。
var a1 = ['マイカル', '平和堂'];a1[0] = 'イオン';a1;// => [ 'イオン', '平和堂' ]
ループ
配列のループにはいくつかの方法があります。
ひとつめの方法は for
ループを使った方法です。
for
ループは次のいくつかの形で使用できます。
// for + インデックスvar a1 = ['パンダ', 'キリン', 'ライオン'];for (let i = 0; i < a1.length; i++) {console.log(a1[i]);}// =>// 'パンダ'// 'キリン'// 'ライオン'
// for + インデックス その 2// `length` へのアクセスを一度きりにしたい場合は次の形にするとよいvar a1 = ['パンダ', 'キリン', 'ライオン'];for (let i = 0, length = a1.length; i < length; i++) {console.log(a1[i]);}// =>// 'パンダ'// 'キリン'// 'ライオン'
// for .. in// インデックスでのループになるvar a1 = ['パンダ', 'キリン', 'ライオン'];for (let i in a1) {console.log(a1[i]);}// =>// 'パンダ'// 'キリン'// 'ライオン'
// for .. of// 値でのループになるvar a1 = ['パンダ', 'キリン', 'ライオン'];for (let item of a1) {console.log(item);}// =>// 'パンダ'// 'キリン'// 'ライオン'
インデックスと値を同時に取得したい場合には for .. of
に entries()
メソッドを組み合わせるやり方があります。
// for .. of と entries() の併用var a1 = ['パンダ', 'キリン', 'ライオン'];for (let [index, item] of a1.entries()) {console.log(index, item);}// =>// 0 'パンダ'// 1 'キリン'// 2 'ライオン'
もうひとつの方法は配列の forEach()
メソッドを使った方法です。
// forEachvar a1 = ['パンダ', 'キリン', 'ライオン'];// 値のみを使用するa1.forEach(item => { console.log(item); });// =>// 'パンダ'// 'キリン'// 'ライオン'// 値とインデックスの両方を使用するa1.forEach((item, i) => { console.log(item, i); });// =>// 'パンダ' 0// 'キリン' 1// 'ライオン' 2
要素の追加
要素を追加する場合は、追加したい位置によって push()
・ unshift()
・ splice()
の 3 つを使い分けます。
末尾の場合は push()
、先頭の場合は unshift()
、途中の場合は splice()
です。
push()
は次の形で使用します。
戻り値は要素追加後の要素数です。
var a1 = ['ラクダ', 'ダチョウ'];a1.push('ウシ');// => 3a1;// => [ 'ラクダ', 'ダチョウ', 'ウシ' ]
push()
には複数の要素を渡すこともできます。
var a1 = ['ラクダ', 'ダチョウ'];a1.push('ウシ', 'シマウマ');// => 4a1;// => [ 'ラクダ', 'ダチョウ', 'ウシ', 'シマウマ' ]
...
構文と組み合わせると、他の配列の要素をすべて追加することもできます。
var a1 = ['ラクダ', 'ダチョウ'];var a2 = ['ウシ', 'シマウマ'];a1.push(...a2);// => 4a1;// => [ 'ラクダ', 'ダチョウ', 'ウシ', 'シマウマ' ]
unshift()
は次の形で使用します。
戻り値は要素追加後の要素数です。
var a1 = ['ウシ', 'シマウマ'];a1.unshift('ラクダ', 'ダチョウ');// => 4a1;// => [ 'ラクダ', 'ダチョウ', 'ウシ', 'シマウマ' ]
splice()
は次の形で使用します。
戻り値は取り除かれた要素からなる配列です。
var a1 = ['ラクダ', 'シマウマ'];var start = 1;var deleteCount = 0;a1.splice(start, deleteCount, 'ダチョウ', 'ウシ');// => []a1;// => [ 'ラクダ', 'ダチョウ', 'ウシ', 'シマウマ' ]
slice()
は slice(start, deleteCount, item1, item2, ...)
という形で使用します。
第一引数 start
は挿入位置のインデックス、第二引数 deleteCount
は元の配列から取り除く要素の数です。
今回のように単純に挿入するだけの場合は deleteCount
は 0
にします。
要素の取り出し
要素の取り出し(=インデックスを使った要素の削除)をする場合は、取り出したい要素の位置によって pop()
・ shift()
・ splice()
の 3 つを使い分けます。
末尾の場合は pop()
、先頭の場合は shift()
、途中の場合は splice()
です。
pop()
は次の形で使用します。
戻り値は取り出した要素の値です。
var a1 = ['あんぱん', 'ジャムパン', 'ピーナツバターパン'];a1.pop();// => 'ピーナツバターパン'a1;// => [ 'あんぱん', 'ジャムパン' ]
shift()
は次の形で使用します。
戻り値は取り出した要素の値です。
var a1 = ['あんぱん', 'ジャムパン', 'ピーナツバターパン'];a1.shift();// => 'あんぱん'a1;// => [ 'ジャムパン', 'ピーナツバターパン' ]
splice()
は次の形で使用します。
戻り値は取り除かれた要素からなる配列です。
var a1 = ['あんぱん', 'ジャムパン', 'ピーナツバターパン'];var start = 0;var deleteCount = 2;a1.splice(start, deleteCount);// => [ 'あんぱん', 'ジャムパン' ]a1;// => [ 'ピーナツバターパン' ]
配列を破壊しない要素の取り出し
元の要素を破壊せずに要素を取り出すには、アンパッキングの記法を使用します。
var a1 = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne'];var [first] = a1;first;// => 'H'var [first, second] = a1;first;// => 'H'second;// => 'He'
スプレッド構文( ...
)を組み合わせると、残りの要素をすべて格納した配列を得ることができます。
var [first, second, ...rest] = a1;rest;// => [ 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne' ]
要素の削除
要素を削除する場合は、対象のどのように指定したいかによって使用すべきメソッドが異なります。
インデックスで指定したい場合は splice()
を、値で指定したい場合は indexOf()
と splice()
の組み合わせた方法を使用します。
より汎用的な方法として filter()
を使ったアプローチもあります。
splice()
メソッドの使い方は上で説明しているので割愛します。
特定の値を削除するための、 indexOf()
と splice()
を組み合わせた方法は次のとおりです。
var a1 = ['バス', '新幹線', 'セグウェイ'];var target = '新幹線';var index = a1.indexOf(target);if (index > -1) {a1.splice(index, 1);// => [ '新幹線' ]}a1;// => [ 'バス', 'セグウェイ' ]
filter()
を使うと、より柔軟な形で要素の削除を行うことができます。
// 特定の値をすべて削除するvar a1 = ['バス', '新幹線', 'セグウェイ', '新幹線', 'バス'];var target = '新幹線';a1 = a1.filter(item => item !== target);// => [ 'バス', 'セグウェイ', 'バス' ]
// インデックス指定で削除するvar a1 = ['バス', '新幹線', 'セグウェイ'];var targetIndex = 1;a1 = a1.filter((item, index) => index !== targetIndex);// => [ 'バス', 'セグウェイ' ]
尚、キーワード delete
を使った方法でも配列の要素を削除することができますが、 delete
を使うと「 length
の値が削除前と変わらない」「インデックスに歯抜けが生じる」という少しトリッキーな挙動になります。
delete
は配列の要素の削除には使わないようにした方がよいです。
// delete を使って要素を削除すると...var a1 = ['バス', '新幹線', 'セグウェイ'];delete a1[1];// '新幹線' が削除されたa1;// => [ 'バス', <1 empty item>, 'セグウェイ' ]// しかし length は 3 のままa1.length;// => 3// `Array.from()` で復元すると要素数は 3 の配列のままArray.from(a1.entries());// => [ [ 0, 'バス' ], [ 1, undefined ], [ 2, 'セグウェイ' ] ]
要素が含まれるかどうかの判定
要素が含まれるかどうかを判定するには、条件指定の方法によって includes()
と some()
を使います。
要素の値を指定して判定する場合は includes()
を、条件を表す関数を渡して判定する場合は some()
を使います。
includes()
は次の形で使用します。
var a1 = ['太陽', '月', '金星'];a1.includes('金星');// => truea1.includes('土星');// => false
some()
は次の形で使用します。
var a1 = [{en: 'Sun', ja: '太陽'},{en: 'Moon', ja: '月'},{en: 'Venus', ja: '金星'},];a1.some(item => item.en === 'Sun');// => truea1.some(item => item.en === 'Mercury');// => false
尚、 some()
の兄弟のようなメソッドに every()
というものがあります。
some()
は配列の中に条件を満たす要素が 1 つでもあれば true
を返しますが、 every()
はすべての要素が条件を満たす場合にのみ true
を返します。
要素のインデックスの取得
特定の要素のインデックスを取得するには indexOf()
または findIndex()
を使用します。
要素の値を指定して判定する場合は indexOf()
を、条件を表す関数を渡して判定する場合は findIndex()
を使います。
indexOf()
は次の形で使用します。
戻り値は、一致する要素が見つかった場合はそのインデックス、見つからなかった場合は -1
です。
var a1 = ['花', '星', '雪'];a1.indexOf('星');// => 1a1.indexOf('宙');// => -1
findIndex()
は次の形で使用します。
戻り値のルールは indexOf()
と同じです。
var a1 = ['花', '星', '雪'];a1.findIndex(item => ['光', '雪'].includes(item));// => 2a1.findIndex(item => ['氷', '土'].includes(item));// => -1
条件を満たす要素の取得
条件を満たす要素を取得するには find()
を使用します。
find()
は次の形で使用します。
var a1 = [{name: 'JavaScript', age: 24},{name: 'Java', age: 24},{name: 'Python', age: 28},{name: 'Kotlin', age: 8},{name: 'Swift', age: 5},];a1.find(item => item.age < 10);// => { name: 'Kotlin', age: 8 }
尚、 find()
は条件に合致した最初の要素を返しますが、 filter()
は条件に合致した要素をすべて返します。
要素のスライス
要素のスライス(特定のインデックス区間の要素の取得)には slice()
を使用します。
var a1 = ['北海道', '青森', '山形', '岩手'];var begin = 2;var end = 3;a1.slice(begin, end);// => [ '山形' ]
slice()
では、要素のインデックスが begin
以上 end
未満の要素を含む配列が返されます。
slice()
は splice()
とは異なり、元の配列は破壊されません。
ソート
ソートには sort()
を使用します。
sort()
には比較のロジックを表す関数を渡します。
var a1 = [{name: 'JavaScript', age: 24},{name: 'Java', age: 24},{name: 'Python', age: 28},{name: 'Kotlin', age: 8},{name: 'Swift', age: 5},];var comparator = (l, r) => {if (l.age < r.age) {return -1;} else if (l.age > r.age) {return 1;}return 0;};a1.sort(comparator);// =>// [// { name: 'Swift', age: 5 },// { name: 'Kotlin', age: 8 },// { name: 'JavaScript', age: 24 },// { name: 'Java', age: 24 },// { name: 'Python', age: 28 }// ]
尚、 sort()
に関数を渡さなかった場合は、各要素を文字列に変換した辞書順でソートされます。
これは数字を要素とした配列に使うと直感と異なる動きをするので注意が必要です。
var numbers = [3, 200, 5.5, -3.2, -3];numbers.sort();numbers;// => [ -3, -3.2, 200, 3, 5.5 ]
マップ
マップ処理には map()
を使用します。
var a1 = [{name: 'たこ'},{name: 'いか'},{name: 'さんま'},{name: 'うなぎ'},];var names = a1.map(item => item.name);// => [ 'たこ', 'いか', 'さんま', 'うなぎ' ]
フィルタ
フィルタ処理には filter()
を使用します。
var a1 = [{title: 'たこ', date: new Date('2014-01-23')},{title: 'いか', date: new Date('2015-10-15')},{title: 'さんま', date: new Date('2019-06-27')},{title: 'うなぎ', date: new Date('2020-08-10')},];var threshold = new Date('2018-05-27');var freshers = a1.filter(item => item.date > threshold);// =>// [// { title: 'さんま', date: 2019-06-27T00:00:00.000Z },// { title: 'うなぎ', date: 2020-08-10T00:00:00.000Z }// ]
畳み込み
畳み込み処理には reduce()
を使用します。
var lines = ['一行が丘の上についた時、','彼等は、言われた通りに振返って、','先程の林間の草地を眺ながめた。','忽ち、','一匹の虎が草の茂みから道の上に躍り出たのを彼等は見た。',];lines.reduce((accum, item) => accum + ' / ' + item);// =>// '一行が丘の上についた時、 / 彼等は、言われた通りに振返って、 / 先程の林間の草地を眺ながめた。 / 忽ち、 / 一匹の虎が草の茂みから道の上に躍り出たのを彼等は見た。'
その他
整数のリストの生成
1 から指定された値までの整数を要素に含む配列を生成するには Array.from()
等を使用します。
/*** 1 から指定された整数までを格納した配列を返す*/function range(stop) {return Array.from({ length: stop }, (_, i) => i + 1);}range(0);// => []range(1);// => [1]range(5);// => [1, 2, 3, 4, 5]
0 から指定された値未満の整数までを格納した配列を返す場合は次のようにします。
/*** 0 から指定された整数までを格納した配列を返す(指定された値を含まない)*/function range0(stop) {return Array.from({ length: stop }, (_, i) => i);}range0(0);// => []range0(1);// => [0]range0(5);// => [0, 1, 2, 3, 4]
以上です。
今後必要に応じて改訂していこうと思います。