LoginSignup
55
54

More than 3 years have passed since last update.

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

Last updated at Posted at 2017-03-30

なんとなく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を含むイテレータを返す
  • JavaScriptでイテレータを使ってるところを殆ど見たこと無いのでなんとも言えない
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

参考

55
54
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
55
54