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); // 👈 とってもシンプル!
開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

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

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

ではでは〜❗

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

このエントリーをはてなブックマークに追加       follow us in feedly