JavaScript
array

JavaScriptのArrayの機能が思ったよりたくさんあった件について

More than 1 year has passed since last update.

なんとなくMDN(Mozilla Developer Network)のドキュメントを覗いてみると、そこには大量のメソッドが...!

「forで回せばどうにでもなるんや!」から卒業して、mapとかreduce使って今風というか関数型チックなコーディングをしたかったので、手始めにArrayのプロパティやらメソッドやらをまとめてみました。

サンプルコードはほぼMDNのドキュメントからのコピペです。


プロパティ


length


  • Arrayの要素数を返す

const array = [1, 2, 3];

console.log(array.length); // 3


prototype

Array.isArray(Array.prototype); // true


メソッド(非prototype)


Array.from


  • Arrayっぽいもの(Array, string, Set, Map etc...)からArrayを生成する

// Array-like object (arguments) to Array

function f() {
return Array.from(arguments);
}

f(1, 2, 3);
// [1, 2, 3]

// Any iterable object...
// Set
var s = new Set(["foo", window]);
Array.from(s);
// ["foo", window]

// Map
var m = new Map([[1, 2], [2, 4], [4, 8]]);
Array.from(m);
// [[1, 2], [2, 4], [4, 8]]

// String
Array.from("foo");
// ["f", "o", "o"]

// 第二引数にmapファンクションを渡すことで、Array.mapっぽいこともできる
Array.from([1, 2, 3], x => x + x);
// [2, 4, 6]

// Generate a sequence of numbers
Array.from({length: 5}, (v, k) => k);
// [0, 1, 2, 3, 4]


Array.isArray


  • 任意のオブジェクトがArrayか否かを判別する

  • SetやMapはfalse扱い


// 以下の呼び出しはすべてtrueを返す

Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
// あまり知られていないもののArray.prototypeは配列:
Array.isArray(Array.prototype);

// 以下の呼び出しはすべてfalseを返す
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);
Array.isArray(new Set());
Array.isArray(new Map());
Array.isArray({ __proto__: Array.prototype });


Array.of



  • まだ仕様が安定していないため非推奨


    • 仕様確定してました。MDNの日本語版の情報が古かったようです...



  • 引数をつなげてArrayにする

Array.of(1);         // [1]

Array.of(1, 2, 3); // [1, 2, 3]
Array.of(undefined); // [undefined]


Array.observe


  • 廃止されました


メソッド(prototype)


Array.prototype.concat

const new_array = old_array.concat(value1[, value2[, ...[, valueN]]]);


  • Arrayの連結


  • 元のArrayは変更されない

  • 何回も連結する場合はArray.prototype.push.apply()の方が速いらしい


    • concatは毎回コピーを作成するため



const alpha = ['a', 'b', 'c'],

numeric = [1, 2, 3];

const alphaNumeric = alpha.concat(numeric);

console.log(alphaNumeric); // Result: ['a', 'b', 'c', 1, 2, 3]
console.log(alpha); //alphaは変わらない: ['a', 'b', 'c']

// 3つのArrayを連結する時
const num1 = [1, 2, 3],
num2 = [4, 5, 6],
num3 = [7, 8, 9];

const nums = num1.concat(num2, num3);

console.log(nums);
// results in [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 値を配列にぶち込むこともできる
const alphaNumeric2 = alpha.concat(1, [2, 3]);

console.log(alphaNumeric2);
// Result: ['a', 'b', 'c', 1, 2, 3]


Array.prototype.copyWithin

array.copyWithin(target, start[, end = this.length]);


  • 「cppのmemmoveと同様の動きをする」...らしい

  • 正直使いどころがよくわからない

  • memmoveの解説

  • 破壊的メソッドのため、取扱い注意

[1, 2, 3, 4, 5].copyWithin(-2);

// [1, 2, 3, 1, 2]

[1, 2, 3, 4, 5].copyWithin(0, 3);
// [4, 5, 3, 4, 5]

[1, 2, 3, 4, 5].copyWithin(0, 3, 4);
// [4, 2, 3, 4, 5]

[1, 2, 3, 4, 5].copyWithin(0, -2, -1);
// [4, 2, 3, 4, 5]

[].copyWithin.call({length: 5, 3: 1}, 0, 3);
// {0: 1, 3: 1, length: 5}

// ES6 Typed Arrays are subclasses of Array
var i32a = new Int32Array([1, 2, 3, 4, 5]);

i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]

// On platforms that are not yet ES6 compliant:
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]


多分こんな動き

ary.copyWithin(x, y, z)のとき、


  1. ary[x] = ary[y]

  2. ary[x + 1] = ary[y + 1]

  3. 2.をaryの末尾に到達するか、y + nがzになるまで繰り返す

...言語化するって難しい


Array.prototype.entries

const iterator = array.entries();


  • Arrayのiteratorオブジェクトを取得する

  • iteratorは[key, value]の形のようだ

const a = ['a', 'b', 'c'];

const iterator = a.entries();

console.log(iterator.next().value); // [0, 'a']
console.log(iterator.next().value); // [1, 'b']
console.log(iterator.next().value); // [2, 'c']


Array.prototype.every

const allPassed = array.every(callback[, thisObject]);


  • Arrayの各要素に対してテスト関数を実行し、全てtrueだった時のみtrueを返す

  • バリデーションとかに使える


const passed = [12, 5, 8, 130, 44].every((element, index, array) => {
return element >= 10;
});
console.log(passed); // false


Array.prototype.fill

arr.fill(value[, start = 0[, end = this.length]])


  • 開始位置と終了位置を指定して、固定値で埋め尽くす

  • 破壊的メソッド

[1, 2, 3].fill(4);               // [4, 4, 4]

[1, 2, 3].fill(4, 1); // [1, 4, 4]
[1, 2, 3].fill(4, 1, 2); // [1, 4, 3]
[1, 2, 3].fill(4, 1, 1); // [1, 2, 3]
[1, 2, 3].fill(4, -3, -2); // [4, 2, 3]
[1, 2, 3].fill(4, NaN, NaN); // [1, 2, 3]
Array(3).fill(4); // [4, 4, 4]
[].fill.call({ length: 3 }, 4); // {0: 4, 1: 4, 2: 4, length: 3}

[1,2].fill(4, 2); // [1, 2] 元の配列に無いインデックスを指定しても無視される


Array.prototype.filter

const filteredArray = array.filter(callback[, thisObject]);


  • Arrayの各要素に対してテスト関数を実行し、trueだったものだけの新しいArrayを作る

  • 非破壊的なので安心

const filtered = [12, 5, 8, 130, 44].filter((element, index, array) => {

return element >= 10;
});
console.log(filtered); // [12, 130, 44]


Array.prototype.find

const element = arr.find(callback[, thisArg]);


  • Arrayの各要素に対してテスト関数を実行し、trueだった要素を返す

  • trueのものが複数当てはまる場合は最初に見つけたやつを返す


    • オブジェクト配列に対して「特定のプロパティがhogeだったら」みたいなことするときは注意



  • 見つからなかったらundefinedを返す

function isPrime(element, index, array) {

let start = 2;
while (start <= Math.sqrt(element)) {
if (element % start++ < 1) {
return false;
}
}
return element > 1;
}

console.log([4, 6, 8, 12].find(isPrime)); // undefined, not found
console.log([4, 5, 8, 12].find(isPrime)); // 5

// 複数当てはまる場合は最初のやつを返す

function isPrime(element, _index, _array) {
let start = 2;
while (start <= Math.sqrt(element.num)) {
if (element.num % start++ < 1) {
return false;
}
}
return element.num > 1;
}

const testArray = [
{id: 1, num: 4},
{id: 2, num: 5},
{id: 3, num: 7},
{id: 4, num: 8},
{id: 5, num: 12}
];

console.log(testArray.find(isPrime)); //{ id: 2, num: 5 }


Array.prototype.findIndex


  • findと同様だけど、こっちはインデックスを返す

  • 見つからなかったら-1を返す


Array.prototype.forEach

array.forEach(callback[, thisObj]);


  • Arrayの各要素に対して関数を実行する

  • Array.prototype界のスター選手(個人的感想)

  • for文のbreakにあたることはできないので注意


    • continueは途中でreturnすればできる



  • 返値無いけど非破壊的

ffunction logArrayElements(element, index, array) {

if (index % 2 === 1) {
// インデックスが奇数なら飛ばす
// for文でいうところのcontinue;
return;
}
console.log("a[" + index + "] = " + element);
}

[2, 5, 9].forEach(logArrayElements);
// logs:
// a[0] = 2
// a[2] = 9


Array.prototype.includes

const boolean = array.includes(searchElement[, fromIndex]);


  • Arrayに特定の要素が含まれているかをチェックする

  • 仕様未確定

[1, 2, 3].includes(2);     // true

[1, 2, 3].includes(4); // false

// 第二引数でArrayのどのインデックスから調べるかを指定する
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
[1, 2, NaN].includes(NaN); // true


Array.prototype.indexOf

const index = array.indexOf(searchElement[, fromIndex]);


  • Arrayの要素のインデックスを調べる

  • 判定は===演算子と同様の動き


    • よってオブジェクトの配列に対してはfindIndexでやったほうがいい



  • 見つからないと-1を返す

const array = [2, 5, 9];

let index = array.indexOf(2); // index は 0 となる

index = array.indexOf(7); // index は -1 となる


Array.prototype.join

const str = arr.join([separator = ',']);


  • 配列の各要素を連結した文字列を返す

  • デフォルトはカンマ区切り

  • null や undefinedは空文字列に変換される

  • CSV作りたいときに大活躍

const a = ["花", "太陽", "雨"];

const myVar1 = a.join(); // myVar1 に "花,太陽,雨" を代入
const myVar2 = a.join(", "); // myVar2 に "花, 太陽, 雨" を代入
const myVar3 = a.join("と"); // myVar3 に "花と太陽と雨" を代入
const myVar4 = a.join(''); // myVar4 に "花太陽雨" を代入


Array.prototype.keys

const iterator = array.keys();


  • 配列の各インデックスのキーを含む新しい Array Iterator オブジェクトを返す


    • ぶっちゃけよくわかってない



  • 仕様未確定

const arr = ["a", "b", "c"];

const iterator = arr.keys();

console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

// 抜けのあるArrayからも漏れなくキーを取れる
const arr2 = ["a", , "c"];
const sparseKeys = Object.keys(arr2);
const denseKeys = [...arr2.keys()];
console.log(sparseKeys); // ['0', '2']
console.log(denseKeys); // [0, 1, 2]


Array.prototype.lastIndexOf

const index = array.lastIndexOf(searchElement[, fromIndex]);


  • Arrayの要素のインデックスを後ろから調べる

  • 判定の仕方はindexOfと同様

  • 見つからなかったら-1を返すのも同様

const array = [2, 5, 9, 2];

const index = array.lastIndexOf(2); // index is 3
index = array.lastIndexOf(7); // index is -1
index = array.lastIndexOf(2, 3); // index is 3
index = array.lastIndexOf(2, 2); // index is 0
index = array.lastIndexOf(2, -2); // index is 0
index = array.lastIndexOf(2, -1); // index is 3


Array.prototype.map

const mapedArray = arr.map(callback[, thisArg]);


  • Arrayの各要素に対して関数を実行し、その結果を集めた新しいArrayを返す

  • for文で言うところのcontinueやbreakはできない


    • forEachの感覚で途中でreturnするとundefinedが入り込む

    • 処理したくない要素は予めfilterで排除しておくこと



const numbers = [1, 4, 9];

const doubles = numbers.map(function(num) {
return num * 2;
});

// doubles is now [2, 8, 18]
// numbers is still [1, 4, 9]

// 途中でreturnするとundefinedが入り込む
const mapped = [1, 2, 3, 4].map((element, index, array) => {
if (index % 2 === 0) {
return;
}
return element;
});

console.log(mapped); //[ undefined, 2, undefined, 4 ]


Array.prototype.pop()

const tail = array.pop();


  • Arrayの最後の要素を返しつつ、Arrayから削除する

  • 空のArrayに対して使った場合はundefinedが返る

  • 「末尾の値が欲しい時」より「末尾を消したい時」に出番がある気がする

  • 当然破壊的

const myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];

console.log(myFish); // ['angel', 'clown', 'mandarin', 'sturgeon']

const popped = myFish.pop();

console.log(myFish); // ['angel', 'clown', 'mandarin' ]

console.log(popped); // 'sturgeon'


Array.prototype.push()

const newLength = array.push(element1, ..., elementN)


  • Arrayの末尾に要素を追加する

  • 引数を複数与えればそのままの順番で追加される

  • 実は要素追加後の新しいlengthが返ってくる

  • push.applyを使ってArrayの連結もできる


    • その場合push.applyの第一引数のArrayが変更される

    • concatと同じノリでconst newArray = Array.prototype.push.apply(array1, array2);とかやると死ぬ



  • 至極当然破壊的

const sports = ['soccer', 'baseball'];

const total = sports.push('football', 'swimming');

console.log(sports); // ['soccer', 'baseball', 'football', 'swimming']
console.log(total); // 4

const vegetables = ['parsnip', 'potato'];
const moreVegs = ['celery', 'beetroot'];

// 1 つ目の配列に 2 つ目の配列をマージさせます
// vegetables.push('celery', 'beetroot'); と同じ結果になります
Array.prototype.push.apply(vegetables, moreVegs);

console.log(vegetables); // ['parsnip', 'potato', 'celery', 'beetroot']


Array.prototype.reduce

const result = array.reduce(callback[, initialValue]);


  • 各要素に対して順番に関数を適用して、単一の値を導く


    • 数値配列の合計を求めたりするときに便利



  • 地味に有能な子

const sum = [0,1,2,3,4].reduce((previousValue, currentValue, index, array) => {

return previousValue + currentValue;
});
console.log(sum); // 10


previousValue, currentValueってなんやねん

↑の例の場合

1. Arrayの最初の2つの要素を使って、previousValue = 0, currentValue = 1として計算、 0 + 1 = 1がreturnされる

2. previousValue = 1(1.の計算結果), currentValue = 2(Arrayの3つめの要素)として計算、 1 + 2 = 3がreturnされる

3. previousValue = 3(2.の計算結果), currentValue = 3(Arrayの4つめの要素)として計算、 3 + 3 = 6がreturnされる

4. previousValue = 6(3.の計算結果), currentValue = 4(Arrayの5つめの要素)として計算、 6 + 4 = 10がreturnされる

5. Arrayの末尾まで到達したので、最終結果として10がreturnされる

↓initialValueを指定した場合

const sum = [0,1,2,3,4].reduce((previousValue, currentValue, index, array) => {

return previousValue + currentValue;
}, 5);
console.log(sum); // 15


  1. previousValue = 5(initialValue), currentValue = 0(Arrayの最初の要素)として計算、 5 + 0 = 5がreturnされる

  2. previousValue = 5(1.の計算結果), currentValue = 1(Arrayの2つめの要素)として計算、 5 + 1 = 6がreturnされる

  3. previousValue = 6(2.の計算結果), currentValue = 2(Arrayの3つめの要素)として計算、 6 + 2 = 8がreturnされる

  4. previousValue = 8(3.の計算結果), currentValue = 3(Arrayの4つめの要素)として計算、 8 + 3 = 11がreturnされる

  5. previousValue = 11(4.の計算結果), currentValue = 4(Arrayの5つめの要素)として計算、 11 + 4 = 15がreturnされる

  6. Arrayの末尾まで到達したので、最終結果として15がreturnされる

実際に一回コードを書いてみればなんとなくわかるかと思います


Array.prototype.reduceRight

const result = array.reduceRight(callback[, initialValue]);


  • reduceと同じことを、Arrayの末尾から順番にやる


Array.prototype.reverse

array.reverse()


  • Arrayの順番を逆転させる

  • 破壊的メソッド

const myArray = [1, 2, 3];

myArray.reverse(); //[3, 2, 1]


Array.prototype.shift

const top = array.shift();


  • Arrayの先頭の要素を返しつつArrayから削除する

  • 先頭の要素を取得できることより、先頭の要素が削除されることがミソ


    • 取得したいだけならarray[0]でいいし、arrayが変更されないから安全



  • 破壊的

const myFish = ["angel", "clown", "mandarin", "surgeon"];

console.log("myFish 処理前: " + myFish);
//myFish 処理前: ["angel", "clown", "mandarin", "surgeon"]

const shifted = myFish.shift();

console.log("myFish 処理後: " + myFish);
//myFish 処理後: ["clown", "mandarin", "surgeon"]

console.log("取り除いた要素: " + shifted);
//取り除いた要素: angel


Array.prototype.slice

const sliced = array.slice(begin[,end]) ;


  • インデックスを指定してArrayの一部を抜き出す

  • endを指定しなかった場合はbeginから末尾まで


  • endに指定したインデックス自体は対象外



    • const sliced = array.slice(1, 3);とした場合、slicedにはarray[1],array[2]は含まれるがarray[3]は含まれない

    • この仕様はトラップすぎると思う



// Our good friend the citrus from fruits example

const fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
const citrus = fruits.slice(1, 3);

// puts --> ["Orange","Lemon"]


Array.prototype.some

const someElementPassed = array.some(callback[, thisObject]);


  • Arrayの各要素に対してテスト関数を実行し、一つでもtrueだったらtrueを返す

  • every -> 全て, some -> どれか、つまりそういうことだ


Array.protoype.sort

array.sort(compareFunction);


  • compareFunctionにもとづいてarrayをソートする


    • compareFunctionを指定しなかった場合は、各要素を無理やりStringにして辞書順にソートする

    • "80"が"9"の前に来ちゃうぞ!



  • 破壊的メソッド


忘れやすいcompareFunctionの仕様


  • 引数a, bはArrayの中の隣り合う2要素

  • compareFunction(a, b) < 0 のとき -> aをbより先に持ってくる

  • compareFunction(a, b) === 0 のとき -> 何もしない


    • ECMAScript標準では非推奨



  • compareFunction(a, b) > 0 のとき -> aをbより後に持ってくる

というわけで、何かの基準で比較して、1 or -1を返すようにするのがベター


Array.prototype.splice

array.splice(index, howMany, [element1][, ..., elementN]);


  • Arrayから要素を削除しつつ、代わりに新しい要素をぶち込む


    • array[index]から順番にhowMany個の要素を消して、代わりにelement1~elementNをぶち込む

    • 当然要素数が変わることもありうる




  • Array.prototype.sliceと間違えたら大惨事

  • 一つのメソッドで色々やりすぎな気がしなくもない

  • これでもかというぐらい破壊的なメソッド

const myFish = ["angel", "clown", "mandarin", "surgeon"];

console.log("myFish: " + myFish); // myFish: angel,clown,mandarin,surgeon

let removed = myFish.splice(2, 0, "drum");
console.log("1 つ追加した後: " + myFish); // 1 つ追加した後: angel,clown,drum,mandarin,surgeon
console.log("取り除いたのは: " + removed);// 取り除いたのは:

removed = myFish.splice(3, 1);
console.log("1 つ取り除いた後: " + myFish); // 1 つ取り除いた後: angel,clown,drum,surgeon
console.log("取り除いたのは: " + removed); // 取り除いたのは: mandarin

removed = myFish.splice(2, 1, "trumpet");
console.log("1 つ置き換えた後: " + myFish); // 1 つ置き換えた後: angel,clown,trumpet,surgeon
console.log("取り除いたのは: " + removed); // 取り除いたのは: drum

removed = myFish.splice(0, 2, "parrot", "anemone", "blue");
console.log("2 つ置き換えた後: " + myFish); // 2 つ置き換えた後: parrot,anemone,blue,trumpet,surgeon
console.log("取り除いたのは: " + removed); // 取り除いたのは: angel,clown


Array.prototype.toLocaleString

const locale = array.toLocaleString();


  • Arrayの各要素を連結して文字列にする、そしてローカライズする

  • すごく...影が薄いです...

const number = 1337;

const date = new Date();
const myArr = [number, date, 'foo'];

const str = myArr.toLocaleString();

console.log(str);
// logs '1337,6.12.2013 19:37:35,foo'(ドイツで実行した場合)


Array.prototype.toSource

const source = array.toSource();


  • Arrayのソースを返す

  • 非標準なので基本的に使っちゃダメ

  • デバッグ目的で使う可能性が微粒子レベルで存在する

const alpha = new Array("a", "b", "c");

alpha.toSource(); // ["a", "b", "c"] が返る


Array.prototype.toString

const string = array.toString();


  • Arrayの各要素をカンマ区切りで連結にして文字列にする

  • join()と同じ


JavaScript 1.8.5 以降、および ECMAScript 第 5 版では、toString() メソッドは一般化されており、全てのオブジェクトで使用可能となっています。オブジェクトが join() メソッドを持つ場合はそれが呼び出され、その値が返されます。そうでない場合は Object.prototype.toString() が呼び出され、その結果の値が返されます。


Mozillaのドキュメントより引用


Array.prototype.unshift

const newLength = array.unshift([element1[, ...[, elementN]]])


  • Arrayの先頭に要素を追加する

  • push, pop, shift, unshiftで混乱するのは誰もが通る道

  • こいつも何気に要素追加後のlengthを返す

  • 破壊的

const arr = [1, 2];

arr.unshift(0); //result of call is 3, the new array length
//arr is [0, 1, 2]

arr.unshift(-2, -1); // = 5
//arr is [-2, -1, 0, 1, 2]

arr.unshift( [-3] );
//arr is [[-3], -2, -1, 0, 1, 2]


Array.prototype.values

const eArr = array.values();


  • valueを含むイテレータを返す

  • JaaScriptでイテレータを使ってるところを殆ど見たこと無いのでなんとも言えない

const arr = ['w', 'y', 'k', 'o', 'p'];

const eArr = arr.values();
console.log(eArr.next().value); // w
console.log(eArr.next().value); // y
console.log(eArr.next().value); // k
console.log(eArr.next().value); // o
console.log(eArr.next().value); // p


参考