Lodashの便利なメソッド7選!

こんにちは❗フリーランス・エンジニアの 九保すこひ です。

さてさて、私も長く開発をさせていただいてますが、好きなことが仕事にできてホントにありがたい限りです😊✨

そして、そんな私の「プログラムを書く際の心得」として、ひとつ意識しているものがあります。

それが、

コードをなるべく書かない

ということです。

なぜなら、コードが少ないと・・・・・

  • バグは少なくなる
  • 開発速度が上がる
  • コードが見やすくなる(保守しようという気になってもらえる)

からです。(とはいえ、さすがに変数名を1文字だけで書いたりはしませんし、インデックスがついたデータは、コードが増えても一旦変数に入れ替えます。そこはバランスですね😊)

そして、コードを少なくするために好んでよく使うJavaScriptパッケージが今回ご紹介する Lodash.js です。

そこで❗

今回は「コード量を減らす」をテーマにして、私がよく使うLodashのメソッド7つをご紹介したいと思います。

※ちなみに、Laravel mixでも初期状態でLodashを使うようになっています。気になる方は、/resources/js/bootstrap.jsをチェックしてみてくださいね。

「コンビニが近いので、
家のプリンタを使わなくなりました😂」

Lodash.jsのバージョン: 4.17.15

_.find() でコレクションから1件取得する

例えば、以下のようなユーザーのコレクション(オブジェクトの配列)があるとします。

const users = [
    { id: 1, name: '太郎' },
    { id: 2, name: '次郎' },
    { id: 3, name: '三郎' },
    { id: 4, name: '四郎' },
    { id: 5, name: '五郎' },
];

この中からid1のもの(=太郎さん)を取得する場合、Lodashを使うとたった1行コードを書くだけで済みます。

const user = _.find(users, { id: 1 }); // 👈 太郎さんのデータ

これが通常のJavaScriptを使って書くと次のようにループを使うことになるので、どうしてもコードが長くなってしまいます。

let user;

for(let i in users) {

    user = users[i];

    if(user.id === 1) {

        break;

    }

}

console.log(user); // 太郎

ちなみに、_.find()はデータの階層が深くても使えます。
次のデータを見てみましょう。

const users = [
    {
        name: '太郎',
        emails: [
            'taro.1@example.com',
            'taro.2@example.com',
        ]
    },
    {
        name: '次郎',
        emails: [
            'jiro.1@example.com',
            'jiro.2@example.com',
        ]
    },
    {
        name: '三郎',
        emails: [
            'saburo.1@example.com',
            'saburo.2@example.com',
        ]
    }
];

では、この中からメールアドレスがtaro.2@example.comのデータ(つまり、太郎さん)を取得する場合を見てみましょう。

const user = _.find(users, {
    emails: ['taro.2@example.com']
});

つまり、元データと同じ構造でパラメータを渡すだけで簡単にデータを取ることが出来るというわけですね。

_.filter()で条件にあうデータだけ取得する

例えば、以下のデータの中から「role」が「owner」のものだけを取得してみましょう。

const users = [
    { id: 1, name: '太郎', role: 'admin' },
    { id: 2, name: '次郎', role: 'owner' },   // 👈 これ
    { id: 3, name: '三郎', role: 'user' },
    { id: 4, name: '四郎', role: 'owner' },   // 👈 これ
    { id: 5, name: '五郎', role: 'owner' },   // 👈 これ
];

この場合、_.filter()を使うとfor()などのループを使わずに実装することができます。

const owners = _.filter(users, user => {

    return (user.role === 'owner'); // 「role」が「owner」

});

つまり、条件式がtrueになるものは残り、falseのものは除外されることになります。

実際に取得できるデータは次のとおりです。

ちなみにES6にもfilterがあって、同じように使うことができます。

const owners = users.filter(user => {

    return (user.role === 'owner');

});

ただし、ES6版のfilter()で注意が必要なのは、以下のようなオブジェクトには使えないということです。

const users = {
    1: '太郎',
    2: '次郎',
    3: '花子',
};

この場合、以下のようにfilter is not a functionというエラーが出ます。

逆にLodash版の場合は、配列/オブジェクト関係なくどちらでも利用ができます。

const filteredUsers = _.filter(users, (name, id) => {

    return name.includes('郎'); // 名前に「郎」がつくもの

});

_.cloneDeep()で階層が深いデータをコピーする

例えば、以下のように3階層あるデータを完全にコピーする場合を見てみましょう。

const data = {
    first: {
        second: {
            third: [
                'value - 1',
                'value - 2',
                'value - 3',
            ]
        }
    }
};

Lodash_.cloenDeep()ならとてもシンプルにコピーをつくることができます。

const copiedData = _.cloneDeep(data); // 👈 これだけでOK

通常のJavaScriptでデータをコピーする場合には、

  1. まずJSON化
  2. そのJSONをパースする

という以下のような方法を使っていましたが、_.cloneDeep()なら見た目もスッキリしています。

// 通常のJavaScript
const copiedData = JSON.parse(JSON.stringify(data));

// もしくは、ES 6
const copiedData = Object.assign({}, data);

ちなみに、以下のように「新しい変数を用意すればいいのでは❓」と考えた方もいらっしゃるかもしれません。

const copiedData = data; // 👈 新しい変数に格納

しかし、この場合、dataに変化があるとcopiedDataも同じ内容に変更されてしまうことになります。

const copiedData = data; // 👈 新しい変数に格納
data.first = 'none'; // 👈 dataだけを変更しても・・・・・
console.log(copiedData); // 👈 copiedDataまで変更されます

実行したコンソール内容はこちらです。

_.chunk()で特定の個数ごとに分割する

例えば、以下のような10件のデータを「3件ごとに分割する」場合です。

const items = [
    { id: 1, name: '商品名 - 1' },
    { id: 2, name: '商品名 - 2' },
    { id: 3, name: '商品名 - 3' },
    { id: 4, name: '商品名 - 4' },
    { id: 5, name: '商品名 - 5' },
    { id: 6, name: '商品名 - 6' },
    { id: 7, name: '商品名 - 7' },
    { id: 8, name: '商品名 - 8' },
    { id: 9, name: '商品名 - 9' },
    { id: 10, name: '商品名 - 10' },
];

実際の例はこうなります。

const chunkedItems = _.chunk(items, 3); // 👈 最大3件ごとに分割

実行したものがこちらです。

なお、なぜ_.chunk()が重宝するかというとBoostrap(バージョン3)で段組みをする場合、1行ごとにclass="row"を挟む必要があるため、一旦_.chunk()で分割する(つまり1行ごとのデータにグループ化する)必要があるためです。

もちろんデータ量が揃っている場合、class="row"を挟む必要はありませんが、例えば画像の高さが揃っていない場合、以下のように「引っかかる」場所がでてきます。

(また、<table> ... </table>内で1行ずつデータがほしいときも便ですね)

では、Vueを使った例を見てみましょう。

<html>
<head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
    <div id="app">
        <div class="row" v-for="items in chunkedItems">
            <!-- 1行ごとに最大3件ループします -->
            <div class="col-xs-4" v-for="item in items">
                <span v-text="item.id"></span>:
                <span v-text="item.name"></span>
            </div>
        </div>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script>

        new Vue({
            el: '#app',
            data: {
                items: [
                    { id: 1, name: '商品名 - 1' },
                    { id: 2, name: '商品名 - 2' },
                    { id: 3, name: '商品名 - 3' },
                    { id: 4, name: '商品名 - 4' },
                    { id: 5, name: '商品名 - 5' },
                    { id: 6, name: '商品名 - 6' },
                    { id: 7, name: '商品名 - 7' },
                    { id: 8, name: '商品名 - 8' },
                    { id: 9, name: '商品名 - 9' },
                    { id: 10, name: '商品名 - 10' },
                ]
            },
            computed: {
                chunkedItems() {

                    return _.chunk(this.items, 3);

                }
            }
        });

    </script>
</body>
</html>

これを実行すると次のようになります。

なお、Bootstrap 4ではclass="row"を省略しても1行ごとに高さを合わせてくれるようです(さすが👍✨)

_.groupBy()でグループ化する

例えば、以下のユーザーを「犬派」「猫派」でグループ化する場合です。

const users = [
    { id: 1, name: '太郎', dogOrCat: '犬派' }, // 🐶
    { id: 2, name: '次郎', dogOrCat: '猫派' }, // 😺
    { id: 3, name: '三郎', dogOrCat: '猫派' }, // 😺
    { id: 4, name: '四郎', dogOrCat: '犬派' }, // 🐶
    { id: 5, name: '五郎', dogOrCat: '猫派' }, // 😺
];

Lodashなら1行でOKです。

const groupedUsers = _.groupBy(users, 'dogOrCat');

これを実行した結果はこうなります。

_.sortBy()で並べ順を変更する

通常Ajaxを使ってデータを取得する場合は、サーバー側で並べ替えをするものですが、取得後に並べ替えをしたい場合もでてきたりします。

そういった場合には_.sortBy()が便利です。

では、次のユーザーデータを「テストの点数が低い順」で並べ替えてみましょう。

const students = [
    { id: 1, name: '太郎', score: 85 },
    { id: 2, name: '次郎', score: 98 },
    { id: 3, name: '三郎', score: 20 },
    { id: 4, name: '四郎', score: 31 },
    { id: 5, name: '五郎', score: 68 },
];

これも1行で実行できます。

const sortedStudents = _.sortBy(students, 'score');

実行結果はこうなります。

そして、逆に「テストの点数が高い順」に並べ替える場合は次のようにreverse()を追加してください。

const sortedStudents = _.sortBy(students, 'score').reverse();

実行結果はこちらです。

_.padStart()で特定の長さの文字列に変換する

例えば、「111」を必ず7桁の長さにして不足している場合はゼロで埋めて「0000111」としたい場合です。

const str = _.padStart('111', 7, '0'); // 0000111

なお、右側にゼロを埋めたい場合は、_.padEnd()を使ってください。

const str = _.padEnd('111', 7, '0'); // 1110000

なお、ES 6にも同じ機能があって、こちらもシンプルにコードを書くことができるのでおすすめです。

const number = '111';
const str = number.padStart(7, '0'); // 0000111

ちなみに昔はこんなカンジで書いてましたね😂

const number = '111';
const str = String('0000000' + number).slice(-7);

おまけ: _.isEmpty()で値が空かどうかをチェックする

_.isEmpty()もとても便利なのですが私の場合、積極的に利用するのがある特定のケースに限られているので次点としました。

そのケースというのが、「オブジェクトが空かどうかをチェックする」というものです。

JavaScriptは少しクセがあって以下のように比較をしてもfalseが返ることになります。

const obj = {};
return (obj === {}); // 👈 これは falseになります。

そのため、オブジェクトが空かどうかをチェックするには次のようにキーの数を数えるというやりかたで対応しますが、正直コードが長いのでめんどうだったりします。

return (Object.keys(obj).length === 0);

それが、_.isEmpty()ならシンプルにチェックできるんですね。

return _.isEmpty(obj); // 👈 とってもシンプル!

おわりに

ということで、今回は使い方によってはコード量を劇的に減らすことができるLodashの便利なメソッド7つをご紹介しました。

今回はたった7つだけでしたが、Lodashにはコレ以外もめちゃくちゃたくさんのメソッドがあるので、ぜひ「宝探し✨」的な感覚で便利なものを探してみてくださいね。

ではでは〜❗

「コロナの自粛が空けて
ストリート・ピアノの再開が増えてきました❗」

この記事が役立ちましたらシェアお願いします😊✨ by 九保すこひ
また、わかりにくい部分がありましたらお問い合わせからお気軽にご連絡ください。
(また、個人レッスンも承ってます👍)
このエントリーをはてなブックマークに追加       follow us in feedly