LoginSignup
164

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-04-05

前回の記事、
4歳娘「パパ、20歳以上のユーザーを抽出して?」
の続きやで!

休日ワイ

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

スクリーンショット 2020-04-04 13.42.28.png

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

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

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

娘(4歳)「ねえ、パパ?」

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

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

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

娘ちゃんの依頼内容

娘「実は、こないだ山で拾ってきた家族風オブジェクトに、子供が増えてて」
娘「オブジェクトの内容が変わっちゃったの」

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

娘「daughterchildrenになっちゃったの」

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

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

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

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

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

ハスケル子もTV会議で参戦

ワイ「もしもし、ハスケル子ちゃん?」

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

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

よめ太郎「(いや早すぎやろ)」

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

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

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

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

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

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

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

まずはObject.values()map()

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

JavaScript
const familyArray =
    Object.values(family)

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

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

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

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

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

JavaScript
const flatFamilyArray =
    familyArray.flat()

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

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

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

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

よめ太郎「(語彙どうなってんねん)」

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

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

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

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

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

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

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

ハスケル子「はい」

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

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

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

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

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

ワイ「せやせや」

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

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

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

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

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

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

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

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

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

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

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

ハスケル子「はい!」

まとめ

  • flat()はネストした配列を1段階フラットにしてくれる。
    (引数を与えることで2段階、3段階フラットにすることも可能)

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

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

ワイ「なるほどな」

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

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

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

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

〜つづく〜

続きはこちら

ワイ「なに!?flatMap()で有給取り放題やと!?」
4歳娘「パパ、そんなときはクロージャが役に立つんじゃない?」

参考文献

注意

もちろんIEでは使えへんから、Babelとか使ってな!

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
164