【TypeScript/JavaScript】配列操作、利用数順に紹介

TypeScript/JavaScriptの配列操作について、私たちのとあるリポジトリ内での利用順に紹介します。
一度も利用されていない操作は載せていないので悪しからず。


|関数|利用数|
|:--|:--|
|map()|267|
|filter()|122|
|push()|120|
|some()|68|
|join()|49|
|reduce()|38|
|flatMap()|33|
|find()|31|
|includes()|24|
|every()|17|
|flat()|13|
|slice()|12|
|forEach()|11|
|reverse()|5|
|sort()|2|
|concat()|2|
|pop()|1|
|splice()|1|
|unshift()|1|

# map

`map`は、与えた関数を配列のすべての要素に適用させて新しい配列を生成します。

```typescript:map
const before = [1, 2, 3];
const after = before.map(v => v * 2);
console.log(after); // [2, 4, 6]
```

# filter

`filter`は、与えられた条件に合致する要素のみに絞り込んだ配列を返します。

```typescript:filter
const before = [1, 2, 3];
const after = before.filter(v => v % 2 === 0);
console.log(after); // [2]
```

:::note
TypeScriptの場合、
型ガードと合わせると便利です。
:::

```typescript:型安全なfilter
const before = [1, undefined, 3, 4, undefined];

// [1, 3, 4]に絞り込めているが推論される型は(number | undefined)[]のまま
const after = before.filter(v => !!v);

// vがnumberであることを返すため、number[]となる
const safeAfter = before.filter((v): v is number => !!v);
```

なお、filterの戻り値はシャローコピーです。

```typescript:シャローコピー
const before = [
    { name: 'aaa', age: 10 },
    { name: 'bbb', age: 20 },
    { name: 'ccc', age: 30 },
];
const after = before.filter(v => v.age >= 20);
console.log(after); // [{ "name": "bbb", "age": 20 }, { "name": "ccc", "age": 30 }] 

// after[0]のnameを変えると、before[1]のnameも変わっている
after[0].name = 'XXX';
console.log(before[1]); // { "name": "XXX", "age": 20 } 
```

# push

`push`は、配列の末尾に要素を追加します。なお、引数は可変長引数なので、1つ以上の要素を追加できます。

```typescript:push
const ary = [1];
ary.push(2);
ary.push(3, 4, 5, 6);
console.log(ary); // [1, 2, 3, 4, 5, 6] 
```

# some, every, includes

似た処理なのでまとめて説明します。

`some`は、与えられた**条件**に**合致する要素が一つ以上あるか**の判定処理です。
`every`は、与えられた**条件**に**すべての要素が合致するか**の判定処理です。
`includes`は、**特定の要素**が配列に**含まれているか**の判定処理です。

```typescript:some, every, includes
const ary = [1, 1, 1, 2];

console.log(ary.some(v => v % 2 === 0));  // true
console.log(ary.every(v => v % 2 === 0)); // false
console.log(ary.includes(2));             // true
```

# join

`join`は、配列のすべての要素を指定した区切り文字で連結した文字列を返します。
なお、区切り文字のデフォルトは`,`となっており、省略が可能です。

```typescript:join
const ary = [1, 2, 3];

console.log(ary.join());    // "1,2,3"
console.log(ary.join(',')); // "1,2,3"
console.log(ary.join('_')); // "1_2_3"
```

# reduce

`reduce`は、配列の要素を順番に処理して、単一の値に累積していく操作です。
`reduce(callbackFn, initialValue)`の形で記述します。

```typescript:reduce
const ary = [1, 2, 3, 4];
const sum = ary.reduce((p, c) => p + c, 0);
console.log(sum); // 10
```

詳しくは、手前味噌ですが、[【TypeScript/JavaScript】配列操作reduce()を救いたい。](https://qiita.com/rokumura7/items/cdfc92dba508bbfb6127)をご参照くださいませ。


# flatMap, flat

`flatMap`の前に`flat`の説明をしてしまう方が分かりやすいので一緒に説明します。

## flat

`flat`は、ネストした配列を指定した深さで結合した新しい配列を生成します。
深さはデフォルト1となっており省略可能です。

```typescript:flat
const ary = [1, 2, [3, [4]]];

console.log(ary.flat());  // [1, 2, 3, [4]]
console.log(ary.flat(2)); // [1, 2, 3, 4]
```

## flatMap

`flatMap`は、与えた関数を配列のすべての要素に適用させた後に深さ1の`flat`を適用する処理です。
つまり、**`ary.map().flat()`と`ary.flatMap()`は同じ結果が得られます**。
（MDNによると、ary.map().flat()よりもary.flatMap()のほうがわずかに効率的らしいです。）

```typescript:flatMap
const ary = [
    "I am a student",
    "He walks his dog every morning",
];

// map
// [["I", "am", "a", "student"], ["He", "walks", "his", "dog", "every", "morning"]] 
const mapAry = ary.map(v => v.split(' '));
console.log(mapAry);

// map + flat
// ["I", "am", "a", "student", "He", "walks", "his", "dog", "every", "morning"] 
const mapFlatAry = ary.map(v => v.split(' ')).flat();
console.log(mapFlatAry)

// flatMap
// ["I", "am", "a", "student", "He", "walks", "his", "dog", "every", "morning"] 
const flatMapAry = ary.flatMap(v => v.split(' '));
console.log(flatMapAry);
```

# find

`find`は、与えられた条件に合致する**最初の要素**を返します。
合致する要素が存在しない場合は`undefined`を返します。

```typescript:find
const ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

console.log(ary.find(v => v % 2 === 0)); // 2
console.log(ary.find(v => v > 100));     // undefined
```

# slice, splice

名前が似てるので一緒に説明します。

`slice`は、配列から指定した範囲の要素で**新しい配列を生成**します。
対して、`splice`は、配列に対して指定した範囲で、要素を追加したり、削除したりすることで、**配列の内容を変更**します。

## slice

`slice`は、`ary.slice(start, end)`の形で記述し、start,endともに省略が可能です。
なお、endで指定したインデックスの要素は含まれません。
返り値は、**取り出された範囲の要素の新しい配列**になります。

```typescript:slice
const ary = [0, 1, 2, 3, 4, 5, 6];

console.log(ary.slice(1, 2)); // [1]
console.log(ary.slice(3));    // [3, 4, 5, 6]
// 元の配列はそのまま
console.log(ary);             // [0, 1, 2, 3, 4, 5, 6]
```

なお、sliceで生成された配列はシャローコピーです。

## splice

`splice`は、`ary.splice(start, deleteCount, itemN)`の形で記述し、deleteCount及びitemNは省略可能です。
`start`が変更開始の位置、`deleteCount`は取り除く要素数、`itemN`は追加する要素です。
`deleteCount`が省略された場合、操作範囲は`start`から末尾までとなります。
返り値は、**取り除かれた要素を含む配列**です。また、操作元の配列からその要素は取り除かれる点に注意してください。

```typescript:splice
const ary = [0, 1, 2, 3, 4, 5, 6];

// インデックス1から要素を2個取り除く
console.log(ary.splice(1, 2));     // [1, 2]
console.log(ary);                  // [0, 3, 4, 5, 6]

// インデックス3から末尾までの要素を取り除く
console.log(ary.splice(3));        // [5, 6]
console.log(ary);                  // [0, 3, 4]

// インデックス1から要素を1個取り除き、そこに99,100,101を追加する
console.log(ary.splice(1, 1, 99, 100, 101)); // [3]
console.log(ary);                  // [0, 99, 100, 101, 4] 
```

# forEach

`forEach`は、配列のすべての要素に順番に与えられた関数を実行します。

```typescript:forEach
const ary = [1, 2, 3];
ary.forEach(v => console.log(v);
```

# reverse

`reverse`は、配列を反転させます。
返り値は、反転した配列ですが、元の配列も反転しています。

```typescript:reverse
const ary = [1, 2, 3];
const reversed = ary.reverse();
console.log(reversed); // [3, 2, 1]
console.log(ary);      // [3, 2, 1]
```

# sort

`sort`は、配列の要素を任意の比較関数に従ってソートします。
返り値は、ソートされた配列ですが、元の配列もソートされています。

```typescript:sort
const ary = [1, 20, 3, 400, 5, 60];

console.log(ary.sort((a, b) => a - b )); // [1, 3, 5, 20, 60, 400]
console.log(ary);                        // [1, 3, 5, 20, 60, 400]
```

# concat

`concat`は、2つ以上の配列を結合して、新しい配列を返します。

```typescript:concat
const ary1 = [1, 2, 3];
const ary2 = [100, 200, 300];

const concatAry = ary1.concat(ary2);
console.log(concatAry); // [1, 2, 3, 100, 200, 300]
console.log(ary1);      // [1, 2, 3]
```

スプレッド構文を用いた書き方のほうが現在は一般的かもしれないですね。

```typescript:スプレッド構文を用いた配列の結合
const ary1 = [1, 2, 3];
const ary2 = [100, 200, 300];

const ary3 = [...ary1, ...ary2];
console.log(ary3); // [1, 2, 3, 100, 200, 300]
```

# pop

`pop`は、配列から最後の要素を取り除き、その要素を返します。

```typescript:pop
const ary = [1, 2, 3, 4, 5];

console.log(ary.pop()); // 5
console.log(ary.pop()); // 4
console.log(ary.pop()); // 3
```

# unshift

`unshift`は、配列の先頭に要素を追加します。

```typescript:unshift
const ary = [1, 2];

ary.unshift(100, 101, 102);
ary.unshift(99);

console.log(ary); // [99, 100, 101, 102, 1, 2]
```

# 参考

[MDN Web Docs - Array](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array)