吉本集の個人ブログ
Web制作の技術について書いています。たまに日記も書きます。

【JS入門】配列について Vol.1(生成、要素の追加・削除)

2018年2月14日 / category : javascript

配列について解説していきます。過去の記事で配列が出てきていましたが、今回の記事で改めて詳しく解説します。
値を順番をつけてまとめたオブジェクトを配列と言います。配列に入った値を要素と呼び、その要素に割り当てられた番号をインデックスと呼びます。このインデックスは0から始まり、最大インデックスは4,294,967,294になります。まずは配列がどのようなオブジェクトなのかを見ていきます。

配列の生成

配列の生成には、配列リテラルで生成する方法と、Arrayオブジェクトのコンストラクタ関数を使用して生成する方法の2パターンあります。

配列リテラルによる配列の生成

const arr = [];

Arrayオブジェクトのコンストラクタ関数による配列の生成

const arr = new Array();

定数を宣言する際に使用するconstで配列を宣言しています。過去の記事「ブロックスコープについて(let, const)」で少し触れていますが、配列に要素を追加することは許可されています。また、要素を削除したり、変更することも許可されていますので、基本的には定数扱い(const宣言)で配列を操作しても問題ないと考えます。次のコードを見てみます。

sample.js
const arr = [];
arr[0] = 100;
console.log( arr[0] ); // 100
arr = []; // Uncaught TypeError: Assignment to constant variable.

コンソール画面

100
Uncaught TypeError: Assignment to constant variable.

配列リテラルで配列を生成しています。続いて、配列arrのインデックス0に100の値を代入しています。参照するときは[インデックス番号]で値を参照できます。ただし、最後のコードで、配列arrに新たに配列を代入しようとしていますが、これはconstで宣言しているのでエラーになります。もしこのようなケースになり得る場合は、varやletなどで宣言する必要があります。

続いて、Arrayオブジェクトのコンストラクタ関数で配列を生成してみます。コンストラクタ関数の引数を設定することで、生成時に配列の長さを決めることができます。これが配列リテラルでの生成との違いです。

sample.js
const arr = new Array();
arr[0] = 100;
console.log( arr[0] ); // 100
console.log( arr.length ); // 1

const arr2 = new Array(1000);
console.log( arr2.length ); // 1000

配列arrについては、先に解説した配列リテラルでの生成をArrayオブジェクトのコンストラクタ関数での生成に置き換えたパターンです。Arrayオブジェクトで定義しているプロパティlengthで配列の長さを参照することができます。配列arrはインデックス0のみの配列ですので、配列の長さは1となります。
配列arr2は、コンストラクタ関数の引数に長さを指定することで、配列の長さを持った配列が生成されます。上のコードの場合、1000の長さを持った配列になっていますが、要素は全て空の状態です。また、コンストラクタ関数の引数に配列データを入れて生成することも可能です。次のコードを見てみます。

sample.js
const arr = new Array('お腹減った', 100, true);
console.log( arr ); // (3) ["お腹減った", 100, true]

Arrayオブジェクトのコンストラクタ関数による配列:引数なし

const arr = new Array();

Arrayオブジェクトのコンストラクタ関数による配列:引数に長さを指定

const arr = new Array(100);

Arrayオブジェクトのコンストラクタ関数による配列:引数に配列データを指定

const arr = new Array('お腹減った', 100, true);

Arrayオブジェクトのコンストラクタ関数を使用して生成する場合は、上の3パターンの生成方法があります。

配列の要素について

続いて配列に入れる要素について解説します。配列に入れる要素の型を統一しなければいけない言語がありますが、JavaScript言語には縛りはありません。次のコードを見てみます。

sample.js
const arr = [];
arr[0] = 'お腹減った。';
arr[1] = 100;
arr[2] = true;
arr[3] = undefined;
arr[4] = null;

let name = '吉本';
arr[5] = name;

const NAME = '吉本';
arr[6] = NAME;

let obj = {
	name: '吉本',
	age: 37
};
arr[7] = obj;

console.log( arr );

コンソール画面

Array(8)
	0: "お腹減った。"
	1: 100
	2: true
	3: undefined
	4: null
	5: "吉本"
	6: "吉本"
	7: {name: "吉本", age: 37}
	length: 8
	__proto__: Array(0)

上のコードのように、ひとつの配列にあらゆる値(データ)を要素に代入することができます。それぞれの要素を参照してみます。

sample.js
console.log( arr[0] ); // "お腹減った。"
console.log( arr[1] ); // 100
console.log( arr[2] ); // true
console.log( arr[3] ); // undefined
console.log( arr[4] ); // null
console.log( arr[5] ); // "吉本"
console.log( arr[6] ); // "吉本"
console.log( arr[7], arr[7]['name'], arr[7].age ); // {name: "吉本", age: 37}, "吉本", 37

for文を使用して配列内の要素を参照する場合は、次のようになります。

sample.js
for( let i=0; i<arr.length; i++ ){
	console.log(i,arr[i]);
};

undefinedと存在しない要素の参照をスキップしたい場合は、次のようになります。

sample.js
const arr = [];
arr[100] = 'お腹減った。';
arr[200] = undefined;
arr[300] = null;

for( let i=0; i<arr.length; i++ ){
	if( arr[i] !== undefined ) console.log(i,arr[i]);
};

コンソール画面

100 "お腹減った。"
300 null

さらにnullもスキップしたい場合は、continue文を使用します。

sample.js
for( let i=0; i<arr.length; i++ ){
	if( !arr[i] ) continue;
	console.log( i, arr[i] );
};

コンソール画面

100 "お腹減った。"

また、上の配列のように配列は必ずインデックス0から始めなければいけないというルールはありません。
続いて、次のコードは配列の要素に変数を代入しているコードですが、注意してほしいことがあります。

sample.js
const arr = [];
let message = 'お腹減った。';
arr[0] = message;
message = 'お腹減っていない。';
console.log( arr[0] ); // "お腹減った。"

コンソールには「お腹減った。」が表示されています。「お腹減っていない。」が表示されると思ったかもしれません。配列arrのインデックス0に変数messageを代入したタイミングでは、変数messageの値は「お腹減った。」です。配列arrの要素に代入した後に、変数messageの値に「お腹減っていない。」を代入していますが、配列arrの要素には影響ありません。あくまで変数の値を配列の要素に代入しており、変数を代入しているわけではありませんので、注意が必要です。

多次元配列

配列の要素にはあらゆる値を代入することが可能です。配列の要素に配列を入れることも可能です。配列の中に配列が入っている状態を配列の入れ子と表現しますが、これを多次元配列と呼ぶこともあります。

sample.js
const arr = [];
const arr2 = ['お腹減った。', 100, true];
arr[0] = arr2;

console.log( arr[0][0] ); // "お腹減った。"
console.log( arr[0][1] ); // 100
console.log( arr[0][2] ); // true

上のコードは、配列の入れ子の例になります。配列[インデックス][インデックス]の形で参照することができます。
配列の入れ子の注意点として、次のコードを見てみます。

sample.js
const arr = [];
const arr2 = ['お腹減った。', 100, true];
arr[0] = arr2;
console.log( arr[0][0] ); // "お腹減った。"
arr2[0] = 'お腹減っていない。';
console.log( arr[0][0] ); // "お腹減っていない。"

配列arrのインデックス0に配列arr2を代入した後、配列arr2のインデックス0の要素を変更しました。その後に配列arrを参照すると要素が変更されています。これは先ほどの変数を代入したケースとは違い、配列を参照しているため配列の要素を変更すると参照される要素も変更されます。

sample.js
const arr = [];
const arr2 = ['お腹減った。', 100, true];
arr[0] = arr2;
arr[0][0] = 'お腹減っていない。';
console.log(arr2[0]); // "お腹減っていない。"

こちらのコードの方がわかりやすいかもしれません。入れ子として代入した配列arr2の要素を配列arrからの参照で変更しています。変更後、配列arr2から参照すると要素が変更されていることが確認できます。

配列の要素を追加する

配列はArrayオブジェクトのプロトタイプを継承しています。Arrayオブジェクトには配列を操作するメソッドが定義されています。その中で主なメソッドをいくつか解説していきます。
まずは、配列の要素を追加するメソッドpushを使ったコードを見てみます。

array.push(value1,value2,…,valueN):配列の末尾に要素を追加する
引数 valueN : 追加する要素
戻り値(return): 追加後の配列の長さ(number)
構文

let len = array.push('お腹減った。');

sample.js
const arr = [];
let len = 100;
for(let i=0; i<len; i++){
	arr.push(i);
};
console.log(arr);

コンソール画面

(100) [0, 1, 2, 3, 4, ...]

上のコードでは、空の配列arrを生成し、for文で100回ループ処理を行っており、その度、メソッドpushを使用して配列の要素を、数値を代入しながら追加しています。これをメソッドpushを使用せずに同様の処理を行う場合、次のようなコードになります。

sample.js
const arr = [];
let len = 100;
for(let i=0; i<len; i++){
	arr[i] = i;
};
console.log(arr);

インデックスを指定して要素を代入しています。メソッドpushを使用した場合と結果は同じです。メソッドpushは配列の末尾に要素を追加するメソッドですが、配列の最初に要素を追加したい場合はメソッドunshiftを使用します。

array.unshift(value1,value2,…,valueN):配列の最初に要素を追加する
引数 valueN : 追加する要素
戻り値(return): 追加後の配列の長さ(number)
構文

let len = array.unshift('お腹減った。');

sample.js
const arr = [];
let len = 100;
for(let i=0; i<len; i++){
	arr.unshift(i);
};
console.log(arr);

コンソール画面

(100) [99, 98, 97, 96, 95, ...]

メソッドunshiftを使用せずに追加する場合、次のような書き方もできます。

sample.js
const arr = [];
let len = 100;
for(let i=0; i<len; i++){
	arr[(len-1)-i] = i;
};
console.log(arr);

配列に要素を追加し、新たに配列を生成する場合はメソッドconcatを使用します。先ほど解説したメソッドpush、unshiftは対象になる配列に対して要素を追加するのに対して、メソッドconcatは、対象になる配列に要素を追加して新しい配列を生成しますので、対象になる配列には変更がありません。

array.concat(value1,value2,…,valueN):配列に要素を追加し、新たらしい配列を生成する
引数 valueN : 追加する要素
戻り値(return): 新たらしい配列(array)
構文

const newArr = array.concat('お腹減った。');

sample.js
const arr = ['お腹減った。', 100, true];
const newArr = arr.concat('お腹減っていない。');
console.log('新しい配列 : ', newArr);
console.log('元の配列 : ', arr);

コンソール画面

新しい配列 :  (4) ["お腹減った。", 100, true, "お腹減っていない。"]
元の配列 :  (3) ["お腹減った。", 100, true]

配列arrに要素を追加した新しい配列newArrが生成されました。配列arrには変更がないことが確認できます。

配列の要素を削除する

配列の要素を削除する場合は、メソッドpopとメソッドshiftがあります。メソッドpopは配列の最後の要素を削除し、メソッドshiftは配列の最初の要素を削除します。戻り値はそれぞれ削除した要素を返します。

array.pop():配列の最後の要素を削除する
戻り値(return): 削除する要素(配列の最後の要素)
構文

let value = array.pop();

array.shift():配列の最初の要素を削除する
戻り値(return): 削除する要素(配列の最初の要素)
構文

let value = array.shift();

sample.js
const arr = ['お腹減った。', 100, true];

let value = arr.pop();
console.log( '削除する要素', value ); // 削除する要素 true
console.log( '削除後の配列', arr ); // 削除後の配列 (2) ["お腹減った。", 100]

value = arr.shift();
console.log( '削除する要素', value ); // 削除する要素 お腹減った。
console.log( '削除後の配列', arr ); // 削除後の配列 [100]

配列の一部を取り出す

array.slice(start, end):配列の一部を取り出す
引数 start(number) : どこから取り出すか
引数 end(number) : どこまで取り出すか
戻り値(return): 取り出した配列(array)
構文

const arr = array.slice(2,3);

sample.js
const arr = ['あ','い','う','え','お'];
const sliceArr = arr.slice(2);
console.log('取り出した配列 : ', sliceArr); // 取り出した配列 :  (3) ["う", "え", "お"]
console.log('元の配列 : ', arr); // 元の配列 :  (5) ["あ", "い", "う", "え", "お"]

メソッドsliceを使用することで配列のデータの一部を取り出すことができます。また、取り出したデータは新しい配列として生成されます。元の配列に変更はありません。
メソッドsliceの引数を1つにした場合は、指定したインデックスから最後までのデータを取り出します。上のコードの場合、インデックス2以降のデータを全て取り出すことになります。

sample.js
const arr = ['あ','い','う','え','お'];
const sliceArr = arr.slice(-2);
console.log(sliceArr); // (2) ["え", "お"]

引数をマイナスの数値にした場合は、後ろから数えて最後までのデータを取り出します。

sample.js
const arr = ['あ','い','う','え','お'];
const sliceArr = arr.slice(1,2);
console.log(sliceArr); // ["い"]

引数を2つ設定した場合は、第1引数のインデックスから第2引数のインデックスの前までのデータを取り出します。上のコードでは、インデックス1(要素「い」)からインデックス2の前(要素「う」の前)までの要素になりますので、結果は「い」のみとなります。第2引数のインデックスは含まないことに注意してください。

sample.js
const arr = ['あ','い','う','え','お'];
const sliceArr = arr.slice(1,-2);
console.log(sliceArr); // (2) ["い", "う"]

第2引数をマイナスにした場合も考え方は先ほどと同様です。上のコードの場合は、インデックス1(要素「い」)から、後ろから数えて2番目の要素(「え」)の前までの要素を取り出すことになります。

配列の要素を削除し、要素を追加する

array.splice(index, len, value1, value2,…, valueN):要素を取り出し、新しい要素を追加する
引数 index(number) : どこから取り出すか
引数 len(number) : 取り出す数
引数 valueN : 追加する要素
戻り値(return): 取り出した配列(array)
構文

const newArr = array.splice(2,1,'文字列');

sample.js
const arr = ['あ','い','う','え','お'];
const newArr = arr.splice(3);
console.log( '元の配列 : ', arr ); // 元の配列 :  (3) ["あ", "い", "う"]
console.log( '新しい配列 : ', newArr ); // 新しい配列 :  (2) ["え", "お"]

メソッドspliceを使用すると配列の要素の削除と、取り出した要素を新しい配列に生成する動作を同時に行えます。上のコードのように引数を1つにした場合は、指定したインデックスから最後までの要素を取り出し、新しい配列を生成します。また、取り出された元の配列にも変更が入ります。

sample.js
const arr = ['あ','い','う','え','お'];
const newArr = arr.splice(3,2);
console.log( '元の配列 : ', arr ); // 元の配列 :  (3) ["あ", "い", "う"]
console.log( '新しい配列 : ', newArr ); // 新しい配列 :  (2) ["え", "お"]

引数を2つ設定した場合は、どこから何個要素を取り出すか、という設定になります。上のコードの場合、インデックス3から2個分要素を取り出します。

sample.js
const arr = ['あ','い','う','え','お'];
const newArr = arr.splice(3,2,'か','き');
console.log( '元の配列 : ', arr ); // 元の配列 :  (3) ["あ", "い", "う", "か", "き"]
console.log( '新しい配列 : ', newArr ); // 新しい配列 :  (2) ["え", "お"]

第3引数以降は追加要素を指定することになります。上のコードの場合は、「か」「き」の要素を元の配列に追加しています。

配列の要素を連結して文字列にする

array.join(separator):配列の要素を連結して文字列にする
引数 separator(string) : 配列の各要素を区切る文字列
戻り値(return): 連結後の文字列(string)
構文

let str = array.join();

sample.js
const arr = ['お','腹','減','っ','た','。'];
let str = arr.join();
console.log(str); // お,腹,減,っ,た,。

メソッドjoinの引数を空のままにすると配列の要素を区切る「,」を含め文字列とします。もし「,」を削除した状態で文字列を連結したい場合は、次のようにします。

sample.js
const arr = ['お','腹','減','っ','た','。'];
let str = arr.join('');
console.log(str); // お腹減った。

文字列を配列にする

先ほどのメソッドjoinの逆で、文字列を配列にする方法としてStringオブジェクトのメソッドsplitがあります。Arrayオブジェクトのメソッドではありませんが、ここで解説しておきます。

string.split(separator):文字列を配列にする
引数 separator(string) : 文字列を区切るための文字列
戻り値(return): 生成された配列(array)
構文

const arr = string.split();

sample.js
let str = 'お腹減った。';
const arr = str.split();
console.log(arr); // ["お腹減った。"]

上のコードでは、メソッドsplitに引数を設定していないため文字列がそのまま配列になりました。生成された配列arrは、長さが1で配列のインデックス0の要素に「お腹減った。」が代入されている配列になります。メソッドsplitの引数に区切り文字列を入れたケースを見てみます。

sample.js
let dir = '/aaaa/bbbb/cccc/ddd.html';
const arr = dir.split('/');
console.log(arr); // (5) ["", "aaaa", "bbbb", "cccc", "ddd.html"]

上のコードでは「/」を区切り文字列として配列にしたケースです。ページのディレクトリを配列にしてから操作するというケースが必要になる場合があります。