lodashの便利関数を使いこなす:実務で頻出のユーティリティ集
概要
lodash は JavaScript のユーティリティライブラリとして長く使われてきました。ES2015 以降の言語仕様の充実で「もう不要では」と語られることも増えましたが、実務のコードを読んでいると、標準機能では地味に面倒な処理を lodash が肩代わりしている場面はまだ多く残っています。
この記事では、実務で頻出する lodash の関数を「なぜネイティブより便利なのか」「どこで使うと効くのか」という観点で整理します。標準機能で十分なものは正直に「標準で足りる」と書き、lodash を使う判断基準もあわせて示します。
環境
- Node.js 18 以降 / モダンブラウザ
- lodash 4.x
- TypeScript 併用
必要な関数だけを個別 import する前提で書きます。
import get from "lodash/get"; import groupBy from "lodash/groupBy"; import debounce from "lodash/debounce";
import _ from "lodash" で全体を取り込むとバンドルサイズが膨らむため、フロントエンドでは特に個別 import を基本にします。
実務で効くユーティリティ
1. get / set — ネストされたオブジェクトの安全なアクセス
API レスポンスの深い階層に触れるとき、途中が undefined だと例外になります。オプショナルチェーンで大半は解決しますが、パスを動的に組み立てたいケースでは get が便利です。
import get from "lodash/get";
const res = { user: { profile: { name: "Taro" } } };
const name = get(res, "user.profile.name", "名無し"); // "Taro"
const age = get(res, "user.profile.age", 0); // 0(欠損)
// パスを動的に組み立てられるのが最大の利点
const key = "profile.name";
const dynamic = get(res, ["user", ...key.split(".")]); // "Taro"
オプショナルチェーン(res?.user?.profile?.name ?? "名無し")で書ける場合はそちらで十分です。パスが実行時に決まるときにだけ get を選ぶ、という判断が実務的です。
2. groupBy / keyBy — コレクションの整形
一覧データをキーで束ねる処理は、集計・表示の前段で頻出します。
import groupBy from "lodash/groupBy";
import keyBy from "lodash/keyBy";
const orders = [
{ id: 1, status: "paid", amount: 1200 },
{ id: 2, status: "pending", amount: 800 },
{ id: 3, status: "paid", amount: 500 },
];
const byStatus = groupBy(orders, "status");
// { paid: [ {id:1}, {id:3} ], pending: [ {id:2} ] }
const byId = keyBy(orders, "id");
// { "1": {id:1,...}, "2": {...}, "3": {...} }
groupBy は reduce で自前実装できますが、毎回書くと読み手の負担になります。「配列 → 辞書」への変換は頻度が高いので、ここは lodash に任せる価値があります。なお keyBy はキーが文字列化されるため、数値 ID を保ちたいなら new Map(orders.map(o => [o.id, o])) を検討してください。
3. debounce / throttle — イベント制御の定番
検索入力のインクリメンタルサーチや、スクロール・リサイズ処理の間引きは、自前実装するとタイマー管理でバグりがちです。ここは lodash が今なお強い領域です。
import debounce from "lodash/debounce";
const onSearch = debounce((q: string) => {
fetchSuggestions(q);
}, 300);
// React の場合、再レンダーで再生成されないよう useMemo でくるむ
const debounced = useMemo(
() => debounce((q: string) => fetchSuggestions(q), 300),
[]
);
// アンマウント時のキャンセルを忘れない
useEffect(() => () => debounced.cancel(), [debounced]);
leading / trailing / maxWait といったオプションが揃っている点が、自前実装との差です。throttle は「一定間隔で必ず実行」なのでスクロール連動 UI に向きます。
4. cloneDeep — 深いコピー
structuredClone が使える環境が増えたため、まずはそちらを検討します。ただし関数や一部の特殊オブジェクトを含むデータでは structuredClone が例外を投げるため、cloneDeep が無難な場面が残ります。
import cloneDeep from "lodash/cloneDeep";
const original = { nested: { list: [1, 2, 3] } };
const copy = cloneDeep(original);
copy.nested.list.push(4);
// original は影響を受けない
判断基準: JSON 相当のデータなら structuredClone。それ以外や既存コードとの一貫性を優先するなら cloneDeep。
5. isEqual — 深い等価比較
オブジェクト同士の中身が同じかを判定したいとき、JSON.stringify の比較はキー順序に依存して壊れます。isEqual は構造を再帰的に比較します。
import isEqual from "lodash/isEqual";
isEqual({ a: 1, b: 2 }, { b: 2, a: 1 }); // true(キー順に依存しない)
isEqual([1, [2, 3]], [1, [2, 3]]); // true
深い比較はコストがあるため、多用は避けて浅い比較で済む設計を優先します。
6. uniqBy / sortBy / orderBy — 重複排除と並び替え
import uniqBy from "lodash/uniqBy";
import orderBy from "lodash/orderBy";
const users = [
{ id: 1, name: "A", age: 30 },
{ id: 1, name: "A2", age: 25 },
{ id: 2, name: "B", age: 40 },
];
uniqBy(users, "id"); // [ {id:1,name:"A"}, {id:2,name:"B"} ](先勝ち)
orderBy(users, ["age", "name"], ["desc", "asc"]); // 複数キー+方向指定
単純な uniq は [...new Set(arr)] で済みますが、キー関数付きの重複排除は標準にないため uniqBy が効きます。orderBy は複数キー+方向指定が一発で書ける点が読みやすさに直結します。
標準機能で足りるもの(あえて使わない判断)
lodash を惰性で使わないために、標準で十分なものを明示しておきます。
| やりたいこと | 標準で足りる書き方 |
|---|---|
| 配列の変換 | map / filter / reduce |
| 単純な重複排除 | [...new Set(arr)] |
| 存在チェック | arr.includes(x) |
| 平坦化 | arr.flat() / arr.flatMap() |
| 既定値の補完 | ?? / ?. |
| オブジェクトの一部抽出 | 分割代入 + 残余 |
これらに lodash を使うと、依存とバンドルサイズを増やすだけになります。
確認方法
importした関数を実際に呼び、期待した戻り値になるかを単体テストで確認します- バンドルサイズは
webpack-bundle-analyzerなどで、lodash全体を取り込んでいないか可視化します - TypeScript では
@types/lodashを入れ、getの戻り値型がanyになりがちな点をキャストで補います
注意点
- 全体 import を避ける:
import _ from "lodash"は tree-shaking が効きにくいため、個別 import かlodash-esを使います getの乱用は型を失う: 静的にパスが分かるならオプショナルチェーンの方が型安全です- 深い比較・深いコピーはコスト:
isEqual/cloneDeepはデータ量に比例して重くなります - ネイティブで代替可能なものは代替する: 言語仕様は年々充実しています。定期的に「これはもう標準で書けないか」を見直すのが健全です
まとめ
lodash は「全部入れて全部使う」時代から、「標準で書けないところだけ選んで使う」時代に移りました。実務で今も効くのは、get(動的パス)、groupBy / keyBy(コレクション整形)、debounce / throttle(イベント制御)、uniqBy / orderBy(キー付き操作)あたりです。
「便利だから使う」ではなく「標準で足りるか」を毎回問い、足りないところにだけ差し込む。この使い分けができると、コードは読みやすくバンドルは軽いまま、lodash の恩恵だけを受け取れます。
