ワイ「なに！？型のズレを吸収できるやと！？」

前回の記事、
[**4歳娘「パパ、20歳以上のユーザーを抽出して？」**](https://qiita.com/Yametaro/items/80c819054a8dca1fdb5d)
の続きやで！

## 休日ワイ

ワイ「最近、コロナウィルスの影響でリモートワークがメインになってしもうたから」
ワイ「家でも快適にお仕事ができるように、**42.5インチ**の特大モニタを買ったで！」
ワイ「やっぱデッカいモニタはええな〜」
ワイ「インデントも**思いっ切り**入れれるわ」

<img width="734" alt="スクリーンショット 2020-04-04 13.42.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295452/fc3e846f-a5a0-9b4f-ee8f-61e2fc516ed1.png">


よめ太郎「さすがに入れすぎやろ・・・」

ワイ「**私のインデントは53です**いうてな」

よめ太郎「いやフリーザ様どこで勝負してんねん」
よめ太郎「しかもインデント**奇数**なんかい」

娘（**4歳**）「ねえ、パパ？」

ワイ「ん？」
ワイ「なんや、娘ちゃん」

娘「その大きいモニタを使って、早速やってほしい作業があるの」

ワイ「おお、ええでええで！」

## 娘ちゃんの依頼内容
娘「実は、[**こないだ**](https://qiita.com/Yametaro/items/80c819054a8dca1fdb5d)山で拾ってきた**家族風オブジェクト**に、子供が増えてて」
娘「オブジェクトの内容が変わっちゃったの」

```javascript:JavaScript
const family = {
    mother: {
        name: "よめ太郎",
        age: 35
    },
    father: {
        name: "やめ太郎",
        age: 37
    },
    children: [
        {
            name: "娘ちゃん",
            age: 4
        },
        {
            name: "ボブ",
            age: 39
        }
    ]
};
```

娘「`daughter`が`children`になっちゃったの」

ワイ「ん？どういうことや？」

娘「前回は娘が1人だけだったから`daughter`っていうオブジェクトだったけど」
娘「子供が1人増えて、`children`という**配列**になっちゃったの」
娘「だから、**20歳以上のユーザー**を上手く抽出できなくなっちゃったの」

ワイ「そ、そうなんか」
ワイ「でも、子供たちはどうせ**20歳未満**なんやから」
ワイ「`mother`と`father`だけ取り出せばええんちゃうの？」

娘「それが・・・弟のボブは養子で**39歳**なの」

ワイ「Oh...ボブ...」
ワイ「ワイの知らん間に子供が増えてるから、少しおかしいなって思ったけど」
ワイ「そういう事情やったんか」
ワイ「これは、ワイだけでは無理や」
ワイ「ハスケル子ちゃんを呼ぼう」

## ハスケル子もTV会議で参戦
ワイ「もしもし、ハスケル子ちゃん？」

ハスケル子「はい」
ハスケル子「私を呼ぶってことは、**ボブ**の件ですね」

ワイ「さすがハスケル子ちゃん、話が早いわ」

よめ太郎「（**いや早すぎやろ**）」

## オブジェクトと配列が混ざっている件

ワイ「`mother`と`father`はオブジェクトなのに」
ワイ「`children`は配列やねん」
ワイ「オブジェクトと配列が混ざってんねん」

娘「そうなの」
娘「そこから、**20歳以上のユーザー**を抽出して」
娘「以下のような`otonaArray`っていう配列を作りたいの」

```javascript:JavaScript
[
    {
        name: "よめ太郎",
        age: 35
    },
    {
        name: "やめ太郎",
        age: 37
    },
    {
        name: "ボブ",
        age: 39
    }
]
```

ハスケル子「うーん」
ハスケル子「オブジェクトと配列が混じってて扱いづらいなんて・・・」
ハスケル子「そもそも**仕様とか設計の問題**のような・・・」
ハスケル子「もっと、サーバサイドでデータを整形してから返したほうが・・・」

ワイ「いや、**仕様**とか**設計**とか**サーバサイド**とかじゃないねん」
ワイ「**山で拾ったオブジェクト**なんやから」
ワイ「もう**ボブは家族**なんやから」

ハスケル子「やむを得ないですね」
ハスケル子「分かりました」
ハスケル子「大人だけを抽出した`otonaArray`という配列を作ればいいんですね」

## まずは`Object.values()`と`map()`

ハスケル子「`family`というオブジェクトを元に配列を作るので」
ハスケル子「まずは`Object.values(family)`ですね」

```javascript:JavaScript
const familyArray =
    Object.values(family)
```

ハスケル子「↑こうですね」
ハスケル子「オブジェクトの**key名**は使わずに**valueだけ**取り出して、配列を作ってくれるメソッドです」
ハスケル子「つまり、`familyArray`の中身は・・・」

```javascript:JavaScript
// familyArrayの中身
[
    { name: "よめ太郎", age: 35 },
    { name: "やめ太郎", age: 37 },
    [
        { name: "娘ちゃん", age: 4 },
        { name: "ボブ", age: 39 }
    ]
]
```

ハスケル子「↑こんな風になります」

ワイ「おお、あとはこの中から**20歳以上のユーザー**を抽出すんねやな」
ワイ「でも、オブジェクトと配列が混じってるけど大丈夫なん？」

ハスケル子「余裕です」
ハスケル子「配列の`flat()`メソッドを使って」
ハスケル子「ネストした配列から**1段階フラットな配列**を作ります」

```javascript:JavaScript
const flatFamilyArray =
    familyArray.flat()
```

ハスケル子「↑こうですね」
ハスケル子「すると・・・」

```javascript:JavaScript
// flatFamilyArrayの中身
[
    { name: "よめ太郎", age: 35 },
    { name: "やめ太郎", age: 37 },
    { name: "娘ちゃん", age: 4 },
    { name: "ボブ", age: 39 }
]
```

ハスケル子「↑こんな感じの`flatFamilyArray`が出来上がります」

ワイ「おお」
ワイ「子供の配列が**1段階**フラットになって、単なるフラットな配列になってフラットやな！」

よめ太郎「（語彙どうなってんねん）」

ワイ「あとは`filter()`で**20歳以上のユーザー**を抽出するだけやな・・・！」

ハスケル子「はい」
ハスケル子「なので、いちいち変数に入れずにまとめて書くと・・・」

```javascript:JavaScript
const otonaArray =
    Object.values(family)
        .flat()
        .filter(person => person.age >= 20);
```

ハスケル子「↑こうですね」
ハスケル子「すると・・・」

```javascript:JavaScript
// otonaArrayの中身
[
    { name: "よめ太郎", age: 35 },
    { name: "やめ太郎", age: 37 },
    { name: "ボブ", age: 39 }
]
```

ハスケル子「↑こんな感じの、大人だけを集めた`otonaArray`が出来あがります」

ワイ「まじか〜」
ワイ「`flat()`メソッド便利やな」

ハスケル子「はい」

ワイ「今までのワイやったら`flat()`メソッドなんて知らんかったから」
ワイ「`Array.isArray()`メソッドを使って」
ワイ「配列ならこう、配列じゃなければこう・・・みたいな感じで」
ワイ「**if文**とかで条件分岐しながら」
ワイ「**配列の詰め直し**をしとったところやわ」

ハスケル子「なるほど」
ハスケル子「つまり・・・」

```javascript:JavaScript
// まず空配列を生成。
const flatFamilyArray = [];

familyArray.forEach(personOrArray => {
    // if文で配列かどうか判断しながら詰め直していく。
    if (Array.isArray(personOrArray)) {
        // 配列の場合はスプレッドで展開してpush。
        flatFamilyArray.push(...personOrArray);
    } else {
        // 配列じゃない場合はそのままpush。
        flatFamilyArray.push(personOrArray);
    }
});
```

ハスケル子「↑こういう感じで処理するってことですよね」

ワイ「せやせや」

ハスケル子「空配列を作って、そこにどんどん詰め直していく・・・」
ハスケル子「そういった、**副作用と手続きの連続**で書くのもまあ」
ハスケル子「分かりやすいといえば分かりやすいので良いんですけど」
ハスケル子「私はやっぱり」

```javascript:JavaScript
const otonaArray =
    Object.values(family)
        .flat()
        .filter(person => person.age >= 20);
```

ハスケル子「↑この、メソッドの連続で書ける**関数型**な感じが好きですね」

ワイ「なるほどなぁ・・・」
ワイ「`flat()`はなんか、**if文を内包**してるようで」
ワイ「**魔法みたい**やな」

ハスケル子「ちょっとそんな感じですよね」
ハスケル子「たとえば・・・」

```javascript:JavaScript
[3].flat() // -> [3]

[[3]].flat() // -> [3]
```

ハスケル子「↑こんな感じで」
ハスケル子「元の値が`3`であろうと`[3]`であろうと」
ハスケル子「一度`[]`で包んで`flat()`してやれば」
ハスケル子「同じ`[3]`になる・・・」
ハスケル子「こういう**型ハック**みたいなの楽しいですよね」

ワイ「なるほどな」
ワイ「`[3]`を`flat()`しても、ただの`3`にはならないんやな」
ワイ「せやから`[3].flat()`してやることで**型を合わせられる**・・・」

ハスケル子「そうなんです」

ワイ「なるほどなぁ・・・」
ワイ「オブジェクトから配列に変換しつつ、フラットにして、フィルタリングする・・・」
ワイ「って一つの流れの中でできるから」
ワイ「なんか**カッコええ**ね」

ハスケル子「はい！」

## まとめ
+ `flat()`はネストした配列を**1段階**フラットにしてくれる。  
（引数を与えることで**２段階、３段階**フラットにすることも可能）

ワイ「↑こんな感じやね！」

ハスケル子「はい」
ハスケル子「今回みたく、オブジェクトと配列を同列に扱わないといけない、なんてのは」
ハスケル子「そもそも**設計が微妙**な感じもしますけど」
ハスケル子「例えば、スクレイピングで取得したデータとかの場合は」
ハスケル子「データの形式を自分で決められないので」
ハスケル子「こういうハックが役に立つこともあるかもしれませんね」
ハスケル子「もし思い出したら、使ってみてください」
ハスケル子「たまに便利ですよ！」

ワイ「なるほどな」

ハスケル子「もっと楽しい、`flatMap()`なんていうメソッドもありますよ」
ハスケル子「`map()`して`flat()`してくれるやつです」
ハスケル子「まるで**関数が変身する**みたいですよ」

ワイ「**関数が変身する**・・・？」
ワイ「どういうこと・・・？」

ハスケル子「それはまた**次回**お話しますね」

ワイ「おお、なんか楽しみやな・・・！」

**〜つづく〜**

## 続きはこちら
→[**ワイ「なに！？flatMap()で有給取り放題やと！？」**](https://qiita.com/Yametaro/items/b922479f3c70d3b089fd)
→[**4歳娘「パパ、そんなときはクロージャが役に立つんじゃない？」**](https://qiita.com/Yametaro/items/7a4521e23520947cc43e)

## 参考文献
+ [Array.prototype.flat() - MDN - Mozilla](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/flat)

## 注意
**もちろんIEでは使えへんから、Babelとか使ってな！**