JavaScript配列の基本

## 配列の基本

- 配列の各要素には０から始まる数字（添字、インデックス）によってアクセスできる。そのため、オブジェクトと異なり順序を持っている。
- 配列には異なる型の要素を入れることができる。
- lengthというプロパティで配列の要素数を知ることができる。
- 配列の最後の要素の添字よりも大きな添字を使って代入を行うと、配列が自動的に大きくなり、値が指定されていない要素にはundefinedが暗黙のうちに代入される。

```jsx
// 配列リテラル
const arr1 = [1, 2, 3]; // 数字の配列
const arr2 = ["one", 2, "three"]; // 異なる型の要素をもつ配列
const arr3 = [[1, 2, 3], ["one", 2, "three"]]; // 配列を要素とする配列
const arr4 = [  // オブジェクトや配列、関数など異なる型を要素としてもつ配列
   { name: "サル", type: "object", luckyNumbers: [5, 7, 13] },
   [
      { name: "キジ", type: "object" },
      { name: "鬼", type: "object" },
   ],
   1,
   function() { return "配列の要素として関数を記憶することもできる"; },
   "three",
];

// 要素へのアクセス
console.log(arr1[0]); // 1
console.log(arr1[2]); // 3
console.log(arr3[1]); // ['one', 2, 'three']
console.log(arr4[1][0]); // { name: 'キジ', type: 'object' }

// 配列の長さ
console.log(arr1.length); // 3
console.log(arr4.length); // 5
console.log(arr4[1].length); // 2

// 配列の長さを大きくする
arr1[4] = 5;
console.log(arr1); // [1, 2, 3, , 5]
console.log(arr1[3]); // undefined
console.log(arr1.length); // 5

// 配列の最後の要素より後ろの添字を使う（代入ではない）->大きさは変わらない
console.log(arr2[10]); // undefined
console.log(arr2.length); // 3

// Arrayコンストラクタ（通常は使われない）
const arr5 = new Array(); // 空の配列を生成
console.log(arr5); // []
const arr6 = new Array(1, 2, 3);
console.log(arr6); // [ 1, 2, 3 ]
const arr7 = new Array(2); // 長さ2の配列（各要素はundefined）
console.log(arr7); // [ ,  ]
console.log(arr7[1]); // undefined
const arr8 = new Array("2");
console.log(arr8); // ['2']
```

## 配列要素の操作

注意が必要なのは、配列内の要素を変更してしまう「破壊的な」メソッドと新しい配列を返すメソッドの違いです。例えば、pushは配列を変更してしまいますが、concatは新しい配列を返します。

### 先頭あるいは最後の要素に対する操作

配列arrの先頭要素のことをarr[0]、最後の要素とはarr[arr.length-1]のことを指します。**全て破壊的なメソッドです。**

- push：配列の最後に要素を追加
- pop：配列の最後の要素を削除
- unshift：先頭に要素を追加
- shift：先頭の要素を削除

pushとunshiftの戻り値は変更後の配列の長さです。一方、popとshiftの戻り値は削除された要素です。

```jsx
let arr = ["b", "c", "d"];

console.log(arr.push("e")); // 4  ←現在の長さ（要素数）
console.log(arr); // [ 'b', 'c', 'd', 'e' ]

console.log(arr.pop()); // e
console.log(arr); // [ 'b', 'c', 'd' ]

console.log(arr.unshift("a")); // 4  ←現在の長さ
console.log(arr); // [ 'a', 'b', 'c', 'd' ]

console.log(arr.shift()); // a
console.log(arr); // [ 'b', 'c', 'd' ]
```

### 複数要素の追加

concat：複数の要素を配列に追加し、配列のコピーを戻す。 **非破壊的なメソッド**です。

```jsx
let arr = [1, 2, 3];

let arr2 = arr.concat(4, 5, 6);
console.log(arr); // [ 1, 2, 3 ]  （← 変更なし。以降も同じ）
console.log(arr2); // [ 1, 2, 3, 4, 5, 6 ]

arr2 = arr.concat([4, 5, 6]); //   （配列を渡す）
console.log(arr2); // [ 1, 2, 3, 4, 5, 6 ]

arr2 = arr.concat([4, 5], 6);
console.log(arr2); // [ 1, 2, 3, 4, 5, 6 ]

arr2 = arr.concat([4, 5], [6, 7]); // 引数は2つでいずれも配列
console.log(arr2); // [ 1, 2, 3, 4, 5, 6, 7 ]

arr2 = arr.concat([4, [5, 6]]); // 引数は配列ひとつでその2番目の要素が配列
console.log(arr2); // [ 1, 2, 3, 4, [ 5, 6 ] ]
```

### 部分配列

slice：ある配列の部分からなる配列を作るメソッド。 **非破壊的なメソッド**

```jsx
let arr = [11, 12, 13, 14, 15];

let arr2 = arr.slice(3); // arr[3]から後ろ
console.log(arr2); // [ 14, 15 ]
console.log(arr); // [ 11, 12, 13, 14, 15 ]  （変更なし。以降も同じ）

arr2 = arr.slice(2, 4); // arr[2]からarr[4]のひとつ前まで
console.log(arr2); // [ 13, 14 ]

arr2 = arr.slice(-2); // 最後から2番目以降
console.log(arr2); // [ 14, 15 ]

arr2 = arr.slice(1, -2); // arr[1]から、最後から2番目のひとつ前まで
console.log(arr2); // [ 12, 13 ]

arr2 = arr.slice(-2, -1); // 最後から2番目から最後から1番目のひとつ前まで
console.log(arr2); // [ 14 ]
```

### 途中の要素の削除や途中への要素の追加

splice：配列の任意の場所を指定して内容を変更することができるメソッド。第一引数が変更を開始する場所、第二引数は削除する要素の数、第三引数以降は追加する要素を指定する。 **破壊的なメソッド**

```jsx
let arr = [1, 5, 7];

let arr2 = arr.splice(1, 0, 2, 3, 4); // arr[1]から2, 3, 4が追加される
console.log(arr); // [ 1, 2, 3, 4, 5, 7 ]
console.log(arr2); // [] ←何も削除されていない

arr2 = arr.splice(5, 0, 6); // arr[5]に6が追加されて、以降ひとつずつ後ろへ
console.log(arr); // [ 1, 2, 3, 4, 5, 6, 7 ]
console.log(arr2); // [] ←何も削除されていない

arr2 = arr.splice(1, 2) // arr[1]から2個削除
console.log(arr); // [ 1, 4, 5, 6, 7 ]
console.log(arr2); // [ 2, 3 ] ←削除された要素

arr2 = arr.splice(2, 1, 'a', 'b'); // arr[2]から1個削除して'a'と'b'をそこに追加
console.log(arr); // [ 1, 4, 'a', 'b', 6, 7 ]
console.log(arr2); // [ 5 ] ←削除された要素
```

### 逆転とソート

reverse：配列の要素を逆順に並び替えるメソッド。 **破壊的なメソッド**

```jsx
let arr = [1, 2, 3, 4, 5];

let arr2 = arr.reverse();
console.log(arr); // [ 5, 4, 3, 2, 1 ]
console.log(arr2); // [ 5, 4, 3, 2, 1 ]  ← reverseはオブジェクト自身を返す

arr.reverse();
console.log(arr); // [ 1, 2, 3, 4, 5 ]
console.log(arr2); // [ 1, 2, 3, 4, 5 ]
```

sort：配列の要素のソートを行うメソッド。基本は昇順。 **破壊的なメソッド**

```jsx
let arr = [5, 3, 2, 4, 1];

let arr2 = arr.sort();
console.log(arr); // [ 1, 2, 3, 4, 5 ]
console.log(arr2); // [ 1, 2, 3, 4, 5 ]

arr2.reverse();
console.log(arr); // [ 5, 4, 3, 2, 1 ]
console.log(arr2); // [ 5, 4, 3, 2, 1 ]
```

sortはソートの時に使う関数を指定することもできます。

```jsx
let arr = [{ name: "Suzanne" }, { name: "Jim" },
	     { name: "Trevor" }, { name: "Amanda" }];

console.log(arr);
arr.sort((a, b) => a.name > b.name);  // nameでソート
console.log("------");
console.log(arr);
arr.sort((a, b) => a.name[1] < b.name[1]); // nameの2文字目で逆順にソート
console.log("------");
console.log(arr);

/* 実行結果 
[ { name: 'Suzanne' },
  { name: 'Jim' },
  { name: 'Trevor' },
  { name: 'Amanda' } ]
------
[ { name: 'Amanda' },
  { name: 'Jim' },
  { name: 'Suzanne' },
  { name: 'Trevor' } ]
------
[ { name: 'Suzanne' },
  { name: 'Trevor' },
  { name: 'Amanda' },
  { name: 'Jim' } ]
*/
```

## 検索

indexOf：引数に指定した値に厳密に等しい要素をもつ最初の添字を返す。見つからなかった場合は-1を返す。

lastIndexOf：同様の最後の添字を返す。見つからなかった場合は-1を返す。

```jsx
const o = { name: "ジェリー" };
const arr = [1, 5, "a", o, true, 5, [1, 2], "9"];

console.log(arr.indexOf("a")); // 2
console.log(arr.lastIndexOf("a")); // 2
console.log(arr.indexOf({ name: "ジェリー" })); // -1

console.log(arr.indexOf("a", 5)); // -1  （"a"をarr[5]から検索する）
console.log(arr.indexOf(5, 5)); // 5
console.log(arr.lastIndexOf(5, 4)); // 1   （arr[4]から左に探す）
console.log(arr.lastIndexOf(true, 3)); // -1  （arr[3]から左に探してもない）
```

findIndex：indexOfと似ているが、比較の際に用いる関数を指定することができる。

```jsx
const arr = [{ id: 5, name: "太郎" }, { id: 7, name: "花子" }];

console.log(arr.findIndex(element => element.id === 5)); // 0  ←idが5ならば条件にマッチ
console.log(arr.findIndex(element => element.name === "花子")); // 1
console.log(arr.findIndex(element => element === 3)); // -1
console.log(arr.findIndex(element => element.id === 17)); // -1
console.log(arr.findIndex(element => element.id === 7)); // 1
```

find：比較に用いる関数を指定する。要素自体が返ってくる。

```jsx
const arr = [{ id: 5, name: "太郎" }, { id: 7, name: "花子" }];

console.log(arr.find(element => element.id === 5)); // { id: 5, name: '太郎' }
console.log(arr.find(element => element.id === 2)); // undefined
```

## map
配列内の要素を変換する。
 **非破壊的なメソッド**

```jsx
const cart = [ { 名前: "iPhone", 価格: 54800}, { 名前: "Android", 価格: 49800}];

const names = cart.map(element => element.名前); // 各オブジェクトの「名前」からなる配列を新たに作る
console.log(names); // [ 'iPhone', 'Android' ]

const prices = cart.map(element => element.価格);
console.log(prices); // [ 54800, 49800 ]

const discountPrices = prices.map(element => element*0.8); // 2割引の価格
console.log(discountPrices); // [ 43840, 39840 ]

const lcNames = names.map(element => element.toLowerCase()); // 小文字にする
// const lcNames = names.map(String.toLowerCase); 
// ↑Firefoxではこれでも動くが、nodeやGoogle Chromeでは動作しない
console.log(lcNames); // [ 'iphone', 'android' ]
```

次の例では2つの配列を合体してオブジェクトを要素とする配列を作っている。

```jsx
const items = ["iPhone", "Android"];
const prices = [54800, 49800];
const cart = items.map((x, i) => ({ 名前: x, 価格: prices[i]}));
console.log(cart);
// [ { '名前': 'iPhone', '価格': 54800 }, { '名前': 'Android', '価格': 49800 } ]
```

## filter
配列から不要な要素を取り去る働きをする。条件にマッチしない要素が削除された配列を返す。 
**非破壊的なメソッド**

```jsx
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];

const result = words.filter(word => word.length > 6);

console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
```

## reduce
配列全体を変換する。mapは配列の各要素を変換するが、reduceは配列全体を変換する。
例えば、配列の全要素の合計を計算したり、平均を計算したりして一つの値に変換する。
reduceに渡される関数は2つの引数を取る。一つはアキュムレータ（a）で、もう一つは現在の配列要素（x）。

```jsx
const arr = [5, 7, 2, 4];

const sum = arr.reduce((a, x) => a += x, 0);
console.log(sum); // 18

const sum2 = arr.reduce((a, x) => a + x, 0); /* 「+=」の「=」 は省略できる */
console.log(sum2); // 18
```

## join

配列の各要素をまとめて、1つの文字列を作る。第一引数はセパレータ（デフォルトは「,」になっている）。
**非破壊的なメソッド**

```jsx
const arr = [1, null, "hello", "world", true, undefined];
delete arr[3];
let result = arr.join();
console.log(result); // 1,,hello,,true,
result = arr.join('');
console.log(result); // 1hellotrue
result = arr.join(' -- ');
console.log(result); // 1 --  -- hello --  -- true -- 
```

## 参考文献
[初めてのJavaScript](https://www.oreilly.co.jp/books/9784873117836/)
[引用コード](https://github.com/mushahiroyuki/ljs3)