【JavaScript】Array, Object周りのメソッドを活用しよう

# はじめに
JavaScriptのArrayやObjectには便利なメソッドが備わっています．
**適切に**使ってコードを読みやすく保ちましょう，というお話です．
個人的によく使うメソッドを紹介していきます．

### アロー関数式
本記事のコードではアロー関数式を用います．アロー関数式は関数式の代替構文です．
以下の2文は同じ意味になります．(他にも書き方は色々あります．)

```js
// 伝統的な関数
let double = function (val) {
    return val*2
}

// アロー関数
let double = (val) => val*2
```

# Arrayのメソッド

### 1. find

引数として与えたテスト関数を配列の先頭要素から適用し，一番最初に合格した(truthyな値を返した)要素を返します．どれも合格しなければ`undefined`を返します．

```js
let ary = [
    {val: 100, name: 'aaa'},
    {val: 200, name: 'bbb'},
    {val: 300, name: 'ccc'},
]
ary.find((elm) => elm.val == 200)
// -> {val: 200, name: 'bbb'}
ary.find((elm) => elm.name == 'ddd')
// -> undefined
```

<!--
```js:do-not-use-method
let target
for (const elm of ary) {
    if (elm.val == 200) {
        target = elm
        break
    }
}
// target -> {val: 200, name: 'bbb'}
```
-->

### 2. findIndex
引数として与えたテスト関数を配列の先頭要素から適用し，一番最初に合格した(truthyな値を返した)要素の位置を返します．どれも合格しなければ`-1`を返します．

```js
let ary = [
    {val: 100, name: 'aaa'},
    {val: 200, name: 'bbb'},
    {val: 300, name: 'ccc'},
]
ary.findIndex((elm) => elm.val == 200)
// -> 1
ary.findIndex((elm) => elm.name == 'ddd')
// -> -1
```

<!--
```js:do-not-use-method
let idx
for (const i in ary) {
    if (ary[i].val == 200) {
        idx = i
        break
    }
}
// idx -> 1
```
-->

### 3. indexOf
引数として与えた要素を先頭から検索し，一番最初に見つけた位置を返します．見つからなければ`-1`を返します．

```js
let ary = [
    {val: 100, name: 'aaa'},
    {val: 200, name: 'bbb'},
    {val: 300, name: 'ccc'},
]
let target = ary[1]
ary.indexOf(target)
// -> 1
```

要素の中身までは見ません．Objectなどの場合は同じインスタンスでなければいけません．

```js
[0,1,2].indexOf(2)
// -> 2
[{val:0}, {val:1}, {val:2}].indexOf({val:2})
// -> -1
```

<!--
```js:do-not-use-method
let target = ary[1]
let idx
for (const i in ary) {
    if (ary[i] === target) {
        idx = i
        break
    }
}
// idx -> 1
```
-->

### 4. includes
引数として与えた要素が配列内に含まれるかどうかを`true`か`false`で返します．

```js
let ary = [
    {val: 100, name: 'aaa'},
    {val: 200, name: 'bbb'},
    {val: 300, name: 'ccc'},
]
let target = ary[1]
ary.includes(target)
// -> true
```

こちらも`indexOf`と同様，要素の中身までは見ません．

```js
[0,1,2].includes(2)
// -> true
[{val: 0}, {val: 1}, {val: 2}].includes({val: 2})
// -> false
```

<!--
```js:do-not-use-method
let target = ary[1]
let flg = false
for (const elm of ary) {
    if (elm === target) {
        flg = true
        break
    }
}
// flg -> true
```
-->

### 5. filter
引数として与えたテスト関数に合格する要素のみを残し，新しい配列を生成して返します．

```js
let ary = [{val: 100}, {val: 150}, {val: 200}, {val: 250}]
ary.filter((elm) => elm.val%100 == 0)
// -> [{val: 100}, {val: 200}]
ary.filter((elm) => elm.val >= 200)
// -> [{val: 200}, {val: 250}]
```

<!--
```js:do-not-use-method
let new_ary = []
for (const elm of ary) {
    if (elm.val == 200) {
        new_ary.push(elm)
    }
}
// new_ary -> [{val: 200, name: 'bbb'}]
```
-->

### 6. map
引数として与えた関数を各要素に適用し，その結果からなる新しい配列を生成して返します．

```js
let ary = [
    {val: 100, name: 'aaa'},
    {val: 200, name: 'bbb'},
    {val: 300, name: 'ccc'},
]
ary.map((elm) => {
    return {val: elm.val/100, name: elm.name[0]}
})
// -> [{val: 1, name:'a'}, {val: 2, name:'b'}, {val: 3, name:'c'}]
```

<!--
```js:do-not-use-method
let new_ary = []
for (const elm of ary) {
    new_ary.push({val: elm.val/100, name: elm.name[0]})
}
// new_ary -> [{val: 1, name:'a'}, {val: 2, name:'b'}, {val: 3, name:'c'}]
```
-->

### 7. every
配列の全要素が，引数として与えたテスト関数に合格するかどうかを`true`か`false`で返します．

```js
let ary = [
    {val: 100, name: 'aaa'},
    {val: 200, name: 'bbb'},
    {val: 300, name: 'ccc'},
]
ary.every((elm) => elm.val%100 == 0)
// -> true
ary.every((elm) => elm.val == 200)
// -> false
```

<!--
```js:do-not-use-method
let flg = true
for (const elm of ary) {
    if (elm.val%100 !== 0) {
        flg = false
        break
    }
}
// flg -> true
```
-->

### 8. some
配列内の少なくとも1つの要素が，引数として与えたテスト関数に合格するかどうかを`true`か`false`で返します．

```js
let ary = [
    {val: 100, name: 'aaa'},
    {val: 200, name: 'bbb'},
    {val: 300, name: 'ccc'},
]
ary.some((elm) => elm.val%100 == 0)
// -> true
ary.some((elm) => elm.val == 200)
// -> true
```

<!--
```js:do-not-use-method
let flg = false
for (const elm of ary) {
    if (elm.val == 200) {
        flg = true
        break
    }
}
```
-->

# Objectのメソッド

### 9. assign
1つ以上のコピー元オブジェクトから列挙可能なプロパティを，コピー先オブジェクトにコピー(シャローコピー)します．

```js
let obj1 = {val: 100, name: 'aaa'}
let obj2 = {val: 200, age: 20}
Object.assign(obj1, obj2)
// obj1 -> {val: 200, name: 'aaa', age: 20}
// obj2 -> {val: 200, age: 20}
```

複数のオブジェクトを結合した「新しい」オブジェクトを作成したい場合は以下のようにも書けます．

```js
let new_obj = {...obj1, ...obj2}
// new_obj -> {val: 200, name: 'aaa', age: 20}
// obj1 -> {val: 100, name: 'aaa'}
// obj2 -> {val: 200, age: 20}
```

### 10. fromEntries
キーと値の組み合わせのリストから，オブジェクトに変換します．
(個人的にこのメソッドは分かりづらいので，for文で書いても良いと思います．)

```js
let items = [['val', 100], ['name', 'aaa'], ['age', 20]]

Object.fromEntries(items)
// -> {val: 100, name: 'aaa', age: 20}
```

for文で書くなら以下．

```js
let items = [['val', 100], ['name', 'aaa'], ['age', 20]]

let obj = {}
for (let [k,v] of items) {
    obj[k] = v;
}
// obj -> {val: 100, name: 'aaa', age: 20}
```

# なぜ用意されているメソッドを使うべきか？

本記事で紹介したメソッドの基本動作は簡単で，少しプログラミングを勉強した方であればfor文などを使って自力で書けると思います．
であればなぜJSに用意されているメソッドをわざわざ覚えて使うのか？
私は以下の理由を挙げます．

1. バグを生んでしまう可能性が減る
1. 何の処理なのか一目で分かる
1. 行数が少なくなり，プログラムの見通しが良くなる

いくら経験を積んでも，間違い(=バグ)は起きます．少しでもバグの発生要因になることは減らしましょう．
また，未来の自分や他人がそのプログラムを見るときのために，わかりやすさは重要です．

JavaScriptの動作は比較的早いですし，ブラウザであれば処理するデータもそこまで大きくないことが多いです．
そのため個人的には，少しぐらい処理が冗長になったとしても，用意されているメソッドを使うべきだと思っています．

# 参考

紹介した以外にもいくつかのメソッドが用意されています．
詳しい仕様などは[MDN Web Docs](https://developer.mozilla.org/ja/)が参考になると思います．

https://developer.mozilla.org/ja/