配列 (Array)

配列はオブジェクトの一種であり、データ型としてはオブジェクトです。

配列の生成

配列を生成して初期化する方法を3つ紹介します。なおサンプルコードは、すべて同一の結果となります。

方法 サンプルコード
コンストラクタ
var a = new Array();
a[ 0 ] = 1.0;
a[ 1 ] = 'text';
引数付きコンストラクタ
var a = new Array( 1.0, 'text' );
var a = new Array( 2 );
a[ 0 ] = 1.0;
a[ 1 ] = 'text';
配列リテラル
var a = [];
a[ 0 ] = 1.0;
a[ 1 ] = 'text';
var a = [ 1.0, 'text' ];

コンストラクタ

Array()コンストラクタ関数を使用します。配列の生成後に未定義の要素に代入することで、配列の要素が生成され初期化されます。

引数付きコンストラクタ

Array()の引数に配列要素を与えることで、配列の生成時に初期化できます。

なお引数に数値を1つだけ指定した場合には、それが配列の長さとみなされ、その個数の未定義の要素を持った配列が生成されます。

配列リテラル (配列イニシャライザ)

全体を角かっこ ( [ ] ) で囲み、値をカンマで区切って記述します。

[ value, value, ... ]

最後の要素の後にカンマ (,) を付けると、IE9より前では要素の数が1つ増加します。これは他の環境とは異なる結果となるため、クロスブラウザとするためには最後にカンマを付けるべきではありません。同様の問題はオブジェクトリテラルにもあります。Trailing commas in JavaScript - Stack Overflow

たとえば配列aが、

var a = [ 1,2,3, ];

のように生成されたとすると、

alert( a.length );
alert( a[ a.length - 1 ] );

としたときの各ブラウザの出力は

ブラウザ a.length a[ a.length - 1 ]
IE8 4 undefined
IE9 3 3
Firefox

のように、異なる結果となります。

要素の追加と削除

要素の追加

未定義の要素に代入することで、要素を追加できます。

a[ 10 ] = 1;

要素の削除

簡単には、削除はdelete演算子で行えます。

var a = [ 1, 2, 3, 4, 5 ];
delete a[ 2 ];

// a [1, 2, undefined, 4, 5]
alert( 2 in a );   // false
alert( a.length ); // 5

こうすることで配列からその要素は除かれますが、配列の大きさは変わりません。一方で、削除する要素にundefinedを代入する方法もありますが、

var a = [ 1, 2, 3, 4, 5 ];
a[ 2 ] = undefined;

// a [1, 2, undefined, 4, 5]
alert( 2 in a );   // true
alert( a.length ); // 5

この方法では要素は削除されず、配列の大きさも変わりません。よって確実に要素を削除するには、後述するsplice()を用います。

var a = [ 1, 2, 3, 4, 5 ];
a.splice( 2, 1 );
// a [1, 2, 4, 5]

すべての要素を削除するならば、新たに作成した空の配列を代入するのが簡単です。

var a = [ 1, 2, 3, 4, 5 ];
a = [];
// a []

最初や最後への要素の追加と削除

配列の最初や最後に要素を追加するならば、次項のunshift()やpush()を使用します。一方で特定の位置へ挿入するならば、splice()を用います。

push()とpop()

push()とpop()で、先入れ後出し (FILO) の機能を実現できます。

位置 操作 メソッド
先頭 追加 unshift
削除 shift
最後 追加 push
削除 pop
array.push( value, ... )
Array.push - JavaScript | MDN

push()では複数の引数を与えることで、多数の項目を一度に追加できます。

var a = [ 1, 2 ];
a.push( 3 );       // [ 1, 2, 3 ]
a.push( 10, 'a' ); // [ 1, 2, 3, 10, 'a' ]

引数に配列が含まれるときには、その配列のまま追加されます。それを要素に展開して追加するにはconcat()を使用します。

var a = [ 1, 2 ];
var b = [ 3, 4 ];
a.push( b ); // [1, 2, [3, 4]]

push()とは逆に、要素を取り除くのがpop()です。

array.pop()
Array.prototype.pop - JavaScript | MDN

pop()では最後の1つの要素が返され、その要素が配列から削除されます。最後の要素まで削除され1つも要素を持たなくなると、undefinedが返されます。

var a = [ 1, 2, 3 ];
a.pop(); // 3が返され、aは[ 1, 2 ]となる

splice()

array.splice( start, deleteCount, value, ... )
Array.splice - JavaScript | MDN

挿入、削除そして置換を行います。

splice()とslice()を混同しない。slice()は配列の一部を切り取るメソッドです。

  • 第1引数だけ指定したとき、start以降のすべての要素を削除します。 ※1
  • 第2引数まで指定したとき、start以降のdeleteCountの数の要素を削除します。
  • 第3引数以上を指定したとき、start以降のdeleteCountの数の要素を削除し、startの位置に第3引数以降の値を追加します。
※1 IE9より前では、第2引数を省略できません。省略した場合、削除は行われず配列がそのまま返されます。

startに負数を指定した場合には、配列の終端から数えた要素となります。たとえば-1とすれば末尾の要素であり、これはarray.length+startの位置と等しくなります。

この関数は削除した要素を含む配列を返します。削除したのが1つの要素だけであっても、その要素を返すのではなく、その要素を含む配列を返します

var a = [ 1, 2, 3, 4, 5 ]としたときの実行結果を、下表にまとめます。

コード 実行結果
a.splice( 3 );
[4,5]を返し、aは[1,2,3]となる
a.splice( -3 );
[3,4,5]を返し、aは[1,2]となる
a.splice( 1, 3 );
[2,3,4]を返し、aは[1,5]となる
a.splice( 1, 1 );
[2]を返し、aは[1,3,4,5]となる
a.splice( 1, 10 );
[2,3,4,5]を返し、aは[1]となる
a.splice( 2, 2, 10 );
[3,4]を返し、aは[1,2,10,5]となる
a.splice( 2, 0, 10 );
[]を返し、aは[1,2,10,3,4,5]となる

要素へのアクセス

インデックスをブラケット表記法で指定することで、配列の要素へアクセスできます。

a[ 0 ];

識別子の先頭に数字を指定することは認められないため、これを

a.0;

のようには記述できません。

インデックスの記述方法

インデックスは内部的に文字列として処理されるため、その記述方法によっては予期せぬ結果を返すことがあります。

var a = [ 1, 2, 3 ];
alert( a[  1  ] ); // 2
alert( a[ '1' ] ); // 2

alert( a[  01  ] ); // 2
alert( a[ '01' ] ); // undefined

このことは、これらのインデックスの記述を文字列に変換してみることで、その違いを確かめられます。

alert( (  1  ).toString() ); // '1'
alert( ( '1' ).toString() ); // '1'

alert( (  01  ).toString() ); // '1'
alert( ( '01' ).toString() ); // '01'

存在しない要素へのアクセス

初期化していない要素や範囲外の要素から読み込むと、undefinedが返されます。

var a = new Array( 1 );
alert( a[ 0 ] ); // undefined
alert( a[ 1 ] ); // undefined

またundefined演算子などで削除された要素も、undefinedを返します。

var a = [ 1, 2, 3 ];
alert( a[ 1 ] ); // 2
delete a[ 1 ];
alert( a[ 1 ] ); // undefined

要素の検索

同値の要素のインデックスの取得

var index = array.indexOf( searchElement[, fromIndex ] );
Array.prototype.indexOf - JavaScript | MDN

このメソッドでは、同値演算子によってsearchElementと等しいことを判定します。

var a = [ 1, 'a', 'A' ];
a.indexOf( 'A' ); // 2
a.indexOf( 'B' ); // -1

このindexOf()は、IE9以降でサポートされます。Browser compatibility - Array.prototype.indexOf() - JavaScript | MDN

要素の存在確認

指定インデックスの要素が存在するかどうかは、in演算子で確認できます。

alert( 2 in [10,20,30] ); // true
alert( 2 in [10,20] );    // false

またはその要素を参照し、undefinedを返すかどうかで判定する方法もあります。

var a = [ 'a', 'b', undefined, 'd' ];

alert( a[1] ); // 'b'
alert( a[2] ); // undefined
alert( a[4] ); // undefined

alert( typeof a[1] === 'undefined' ); // false
alert( typeof a[2] === 'undefined' ); // true
alert( typeof a[4] === 'undefined' ); // true

ただしこのように、その要素が値としてundefinedを持つときは正しく判定できません。

条件に一致する要素が存在するかの確認

関数を利用して、条件に一致する要素が存在するか確認できます。

var someElementPassed = array.some( callback[, thisObject] );
Array.some - JavaScript | MDN

arrayの1つずつの要素を引数として、callbackがくり返し呼ばれます。そのときcallbackがtrueを返した時点で、some()はtrueを返します。すべての要素に対してcallbackがfalseを返すと、some()はfalseを返します。

配列のコピー

配列はオブジェクトであるため、代入演算子では参照のコピーとなります。

var a = [1, 2, 3];
var b = a; // 代入演算子によるコピー

// a [1, 2, 3]
// b [1, 2, 3]

a[0]= 5;

// a [5, 2, 3]
// b [5, 2, 3] 配列aへの変更が、配列bにも反映されている

参照ではなく配列の要素をコピーするには、戻り値で新しい配列を返すメソッドを使用するのが簡単です。それは2つあります。

  • Array.slice() … 指定範囲を切り出した、新しい配列を返す
  • Array.concat() … 引数を連結した、新しい配列を返す

それぞれ次のように使用します。

var newArray = array.slice( 0 );
var newArray = array.concat();

これらはシャローコピーであるため、多次元配列では期待通りにコピーできません。

いずれの方法でも同じ結果が得られますが、その処理速度には違いがあります。copy array slice-vs-concat · jsPerfによる結果を信じるならば、slice(0)の方が高速なようです。

シャローコピーとディープコピー

シャローコピー (Shallow copy)

var a = [ [ 1, 2 ], [ 3, 4 ] ];
var b = a.slice( 0 );

// a [[1, 2], [3, 4]]
// b [[1, 2], [3, 4]]

a[0][0]= 5;

// a [[5, 2], [3, 4]]
// b [[5, 2], [3, 4]] 配列aへの変更が、配列bにも反映されている

ディープコピー (Deep copy)

Object.prototype.clone = function()
{
    var newObj = ( this instanceof Array )? [] : {};

    for( i in this )
    {
        if( i == 'clone' ) continue;

        if( this[ i ] && typeof this[ i ] == 'object' )
        {
            newObj[ i ] = this[ i ].clone();
        }
        else
        {
            newObj[ i ] = this[ i ]
        }
    }
    return newObj;
}

特殊な配列

多次元配列 (Multidimensional Arrays)

JavaScriptは多次元配列をサポートしていないため、配列の要素に配列を持たせることで、それに近い動作を実現します。

var a = new Array();
a[ 0 ] = new Array( 1, 2 );
a[ 1 ] = new Array( 3, 4 );

これは配列リテラルを用いれば、

var a = [ [ 1, 2 ], [ 3, 4 ] ];

のように記述しても同じです。

多次元配列の要素へのアクセスは、[]演算子をくり返すことで行います。たとえば先の配列に、

a[ 1 ][ 0 ];

とアクセスすれば、3が返されます。

連想配列 (associative array)

JavaScriptでは配列のインデックスに文字列を使用できません。よって配列として連想配列はサポートされませんが、オブジェクトのプロパティでこれを代用できます。

インデックスに文字列を使用した場合

仮にインデックスに文字列を使用した場合、それはArrayオブジェクトのプロパティとして認識され、配列の要素とはなりません

以下のコードを実行してみます。

var array = new Array();

array[ 0 ] = 10;
array[ 1 ] = 20;
array[ 'a' ] = 30;
array[ 'b' ] = 40;
array[ 2 ] = 50;

デバッガで確認すると、'a'と'b'は配列として認識されていないことがわかります。また配列のサイズ (array.length) は3となります。

連想配列の並べ替え

オブジェクトのプロパティ名を統一しておくことで、それをキーとしてArray.sort()メソッドでソートできます。

var array = new Array();

array.push( { key: 'alpha', value: 10 } );
array.push( { key: 'beta', value: 20 } );
array.push( { key: 'gamma', value: 5 } );

array.sort( function( a, b ) { return a.value - b.value; } );

配列のすべての値を処理 (for ... in)

for ... inの構文で、PHPなどのforeachに近い処理を実現できます。

変数 (下記の例ではi) に格納されるのは配列の要素ではなく、配列のインデックスもしくは連想配列のキーになります。なお、配列の要素を処理する順序は規定されていないめインデックス順に処理されるとは限らないことにも注意が必要です。

var array = new Array( 1, 2 );
array.foo = 'foo';
array[ 5 ] = 5;
array.push( 'a', 'b' );

for( var x in array )
{
    document.write( array[ x ] + ',' );
}
// 1,2,5,a,b,foo, と出力

このように注意点が多いため、for ... in文はオブジェクトのプロパティをすべて処理する場合だけに限定し、通常は

// arrayは上の定義に同じ
for( var i  = 0; i < array.length; i++ )
{
    document.write( array[ i ] + ',' );
}
// 1,2,undefined,undefined,undefined,5,a,b, と出力

のようにfor文を用いるべきです。

forEach()

JavaScript 1.6以降に対応した環境ならば、forEach()メソッドを利用できます。

array.forEach( callback[, thisArg ] )
Array.prototype.forEach() - JavaScript | MDN

このメソッドではarrayの要素が1つずつ、callbackの関数へ渡されます。

要素の並べ替え (ソート)

配列全体を並べ替えるならば、sort()reverse()という2つの組み込み関数が用意されています。一方で特定の要素の順番のみを入れ替えるならば、次のようにsplice()を用います。

var a = [ 'a', 'b', 'c', 'd', 'e' ];

var target = 4;
var dist = 2;

a.splice( dist, 0, a.splice( target, 1 )[ 0 ] );

// [ "a", "b", "e", "c", "d" ]

この場合、最初の要素を0番目として、その4番目の要素である'e'を、2番目である'c'の前へ移動しています。

sort()

array.sort()
sort | MDN

既定では、要素は文字列としてアルファベット順に並べ替えられます。要素が数値であっても文字列に変換されて比較されるため、この点には留意する必要があります。また順番は、厳密には文字エンコーディングによって決まる順となるため、たとえば['C','b','a']を並べ替えると['C','a','b']となります。

var array = [ 'C', 'b', 'B', 'b', 'a' ];

array.sort(); // ['B','C','a','b','b']

よって大文字/小文字の区別なくアルファベット順とするには、後述の比較関数を用いた上で、String.toUpperCase()で大文字に統一してから比較します。

array.sort( function( a, b )  // ['a','b','B','b','C']
  { return ( a.toUpperCase() > b.toUpperCase() )? 1 : -1; } );

ちなみに比較関数を指定しない場合の比較方法は、次のように関数を指定する場合に等しいです。

array.sort( function( a, b ) { return ( a > b )? 1 : -1; } );

並べ替え方法の指定

array.sort( orderfunc )

比較関数orderfuncの順に並べ替えます。

比較関数には引数を2つ持たせます。この引数には比較対象となる配列の要素が渡されますが、渡される順番はブラウザによって異なります。この問題は1つの配列にsort()をくり返すときに、特に顕在化します。

比較関数に最初に渡される引数
ブラウザ 第1引数 第2引数
Internet Explorer array[ 0 ] array[ 1 ]
Firefox
Chrome array[ 0 ] array[ array.length / 2 ]

比較関数は、戻り値として数値を返す必要があります。

戻り値 並べ替え方法
0より小さな 第1引数が第2引数のになるように並べ替え
0 並べ替えなし
0より大きな 第1引数が第2引数のになるように並べ替え

なお、配列の未定義の要素は必ず配列の最後になるように並べ替えられ、比較関数に渡されることはありません。

数値の場合

たとえば配列の要素が数値の場合、数値順に並べ替えるならば、

array.sort( function( a, b ) { return a - b; } );

のようにします。

比較関数 並べ替え方向
function( a, b ) { return a - b; }
昇順
function( a, b ) { return b - a; }
降順
オブジェクトの場合

配列の要素がオブジェクトで、そのオブジェクトがdataというプロパティ名の数値を持つとします。この場合これを数値順に並べ替えるならば、

array.sort( function( a, b ) { return a.data - b.data; } );

のようにします。

reverse()

array.reverse()

要素を逆順に並べ替えます。

配列の結合 (マージ)

array.concat( [value1[, value2[, ...[, valueN]]]] )
Array.prototype.concat - JavaScript | MDN

このメソッドではarrayは変更されず、arrayにvalueを連結した新しい配列が返されます。valueを指定しないときには、arrayのコピーがそのまま返されます。

var a = [ 1, 2 ];
var b = [ 3, 4 ];

var c = a.concat( b );
// aは[1,2]のまま
// cは[1,2,3,4]

引数の配列は要素に展開されますが、その要素に配列が含まれるときには、その配列までは展開されません。

a.concat( 3,[4,5],6 );   // [1,2,3,4,5,6]が返される
a.concat( [3,4,[5,6]] ); // [1,2,3,4,[5,6]]

ちなみにpush()では、

var a = [ 1, 2 ];
var b = [ 3, 4 ];

var e = a.push( b );
// aが[1,2,[3,4]]となる
// eは3 (aの要素数)

のように、配列はそのまま連結されます。

文字列を連結するならば、String.concat()

重複した要素の削除

組み込みのメソッドとしては提供されないため、自前で処理する必要があります。

Array.filter()で処理する方法

Array.filter()は、その配列の要素を引数としてテスト関数を呼び出します。そしてその関数がtrueを返す要素で、新しい配列を生成します。このときテスト関数では、Array.indexOf()の引数で渡された要素のインデックスを探索し、それが引数の要素のインデックスと合致するか判定します。

var array = [ 1, 'a', 2, 'a', 'あ', 'あ', 1 ];

var uniqueArray = array.filter(
    function( value, index, self )
    {
        // 配列selfの要素valueの最初のインデックスと、indexが等しいならばtrueを返す
        return self.indexOf( value ) == index;
    });

alert( uniqueArray.join() ); // 1,a,2,あ
Remove Duplicates from JavaScript Array - Stack Overflow

オブジェクトのプロパティを利用する方法

オブジェクトに同名のプロパティを設定した場合、後に設定したもので先のそれを上書きします。この仕様を利用することで、重複したプロパティを持たないオブジェクトを作成できます。そしてこのオブジェクトのすべてのプロパティの値を配列にまとめることで、要素に重複のない配列を作成できます。

var array = [ 1, 'a', 2, 'a', 'あ', 'あ', 1 ];
var obj = {};

for( var i = 0; i < array.length; i++ )
{
    obj[ array[ i ] ] = array[ i ];
}

// alert( JSON.stringify( obj ) );
//  {"1":1,"2":2,"a":"a","あ":"あ"}

var uniqueArray = [];
for( var key in obj )
{
    uniqueArray.push( key );
}

alert( uniqueArray.join() ); // 1,2,a,あ
Remove Duplicates from JavaScript Array - Stack Overflow

要素を文字列として連結 (join)

join()メソッドでは、配列の要素を文字列に変換して連結できます。よって、

var text = [ 'a', 'b', 'c' ].join( '' );
// abcが返される

のように文字列を作成できます。一方で、文字列を配列に分割するにはString.split()を使用します。

string = array.join( separator )
Array.join - JavaScript | MDN

separatorを省略すると、「,」を指定したものとみなされます。

[1,2].join(); // "1,2"

連結時には文字列に変換されるため、論理値などもすべて文字列となります。

[true,false,null,'',Math].join(); // "true,false,,,[object Math]"

要素が1つしかない場合にはseparatorは使用されず、その1つの要素が変換されただけの文字列が返されます。

[1].join('&'); // "1"

一方で要素が1つもなけらば、空文字列が返されます。

[].join('&'); // ""

要素の一覧表示

配列を文字列に変換することで、配列のすべての要素を確認できます。これはtoString()を呼び出すことと同義であり、また得られる結果はjoin()で返される文字列と同じです。つまり次の4つの処理は、すべて同じ結果を得ます。

alert( array );
alert( array.toString() );

alert( array.join() );
alert( array.join( ',' ) );

このことからわかるように、join()の引数を変更することで一覧の形式を変更できます。たとえば改行コード (\n) 、もしくはbr要素で連結すると一覧が見やすくなります。

alert( array.join( '\n' ) );

参考

Arrayオブジェクトのメソッド