
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、私も長く開発をさせていただいてますが、好きなことが仕事にできてホントにありがたい限りです
そして、そんな私の「プログラムを書く際の心得」として、ひとつ意識しているものがあります。
それが、
コードをなるべく書かない
ということです。
なぜなら、コードが少ないと・・・・・
- バグは少なくなる
- 開発速度が上がる
- コードが見やすくなる(保守しようという気になってもらえる)
からです。(とはいえ、さすがに変数名を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: '五郎' },
];
この中からid
が1
のもの(=太郎さん)を取得する場合、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
でデータをコピーする場合には、
- まずJSON化
- その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
にはコレ以外もめちゃくちゃたくさんのメソッドがあるので、ぜひ「宝探し」的な感覚で便利なものを探してみてくださいね。
ではでは〜
「コロナの自粛が空けて
ストリート・ピアノの再開が増えてきました」