JavaScript 開発者にとって、Lodash について説明する必要はありません。ただし、ライブラリは膨大であり、圧倒されることがよくあります。もうない!
ロダッシュ、ロダッシュ、ロダッシュ。 。 。どこから始めればいいのでしょう! 🤔
JavaScript エコシステムが初期の頃がありました。そこは、西部開拓時代やジャングルにたとえられるかもしれません。そこでは多くのことが起こっていましたが、日々の開発者のフラストレーションや生産性に対する答えはほとんどありませんでした。
それからロダッシュが現場に入ってきました、そしてそれはすべてを水没させる洪水のように感じました。 Lodash には、並べ替えなどの単純な日常ニーズから複雑なデータ構造変換まで、JS 開発者の生活を至福に変える機能が満載 (過負荷ですらありました!) されていました。
そして、ロダッシュは今日どこにいますか?まあ、当初提供されていたすべての優れた機能がまだあり、その後もいくつかの機能が備わっていますが、JavaScript コミュニティでの人気は失われているようです。なぜ?理由はいくつか考えられます。
- Lodash ライブラリの一部の関数は、大きなリストに適用すると遅くなります (そして今でも)。これが世の中のプロジェクトの 95% に影響を与えることはなかったでしょうが、残りの 5% の影響力のある開発者が Lodash に悪い評判を与え、その影響は草の根にまで波及しました。
- JS エコシステムには、傲慢さが必要以上に一般的になる傾向があります (Golang 関係者についても同じことが言えるかもしれません)。そのため、Lodash のようなものに依存するのは愚かなこととみなされ、人々がそのような解決策を提案すると、StackOverflow などのフォーラムで非難されます (「何?! このようなものにライブラリ全体を使用しますか? 実現するには、
filter()
とreduce()
を組み合わせることができます」単純な関数でも同じことです!」)。 - ロダッシュは古いです。少なくともJS標準では。 2012年に発売されたので、この記事を書いている時点でほぼ10年になります。 API は安定しており、毎年追加されるエキサイティングな機能はそれほど多くありません (単に追加する必要がないからです)。そのため、興奮しすぎている平均的な JS 開発者は退屈してしまいます。
私の意見では、Lodash を使用しないことは、JavaScript コードベースにとって重大な損失です。これは、仕事中に遭遇する日常的な問題に対するバグのない洗練されたソリューションであることが証明されており、これを使用すると、コードがより読みやすく、保守しやすくなります。
そうは言っても、一般的な (またはそうでない) Lodash 関数のいくつかを詳しく見て、このライブラリがどれほど信じられないほど便利で美しいかを見てみましょう。
クローン。 。 。深く!
JavaScript ではオブジェクトが参照によって渡されるため、新しいデータ セットが異なることを期待して何かを複製したい場合、開発者にとって頭痛の種になります。
let people = [
{
name: 'Arnold',
specialization: 'C++',
},
{
name: 'Phil',
specialization: 'Python',
},
{
name: 'Percy',
specialization: 'JS',
},
];
// Find people writing in C++
let folksDoingCpp = people.filter((person) => person.specialization == 'C++');
// Convert them to JS!
for (person of folksDoingCpp) {
person.specialization = 'JS';
}
console.log(folksDoingCpp);
// [ { name: 'Arnold', specialization: 'JS' } ]
console.log(people);
/*
[
{ name: 'Arnold', specialization: 'JS' },
{ name: 'Phil', specialization: 'Python' },
{ name: 'Percy', specialization: 'JS' }
]
*/
私たちが純粋に無邪気で、私たちの善意にも関わらず、元のpeople
配列がその過程でどのように変化したか (Arnold の専門分野がC++
からJS
に変更されました)、基礎となるソフトウェア システムの整合性に大きな打撃を与えたことに注目してください。実際、元の配列の真の (深い) コピーを作成する方法が必要です。
おそらく、これは JS でコーディングする「愚かな」方法だと主張する人もいるでしょう。ただし、現実は少し複雑です。はい、便利な構造化演算子が用意されていますが、複雑なオブジェクトや配列を構造化しようとしたことのある人なら誰でもその痛みを知っています。次に、シリアル化と逆シリアル化 (おそらく JSON) を使用してディープ コピーを実現するというアイデアがありますが、それは読者にとってコードを煩雑にするだけです。
対照的に、Lodash を使用した場合のソリューションが驚くほどエレガントで簡潔であることを見てください。
const _ = require('lodash');
let people = [
{
name: 'Arnold',
specialization: 'C++',
},
{
name: 'Phil',
specialization: 'Python',
},
{
name: 'Percy',
specialization: 'JS',
},
];
let peopleCopy = _.cloneDeep(people);
// Find people writing in C++
let folksDoingCpp = peopleCopy.filter(
(person) => person.specialization == 'C++'
);
// Convert them to JS!
for (person of folksDoingCpp) {
person.specialization = 'JS';
}
console.log(folksDoingCpp);
// [ { name: 'Arnold', specialization: 'JS' } ]
console.log(people);
/*
[
{ name: 'Arnold', specialization: 'C++' },
{ name: 'Phil', specialization: 'Python' },
{ name: 'Percy', specialization: 'JS' }
]
*/
ディープ クローン作成後もpeople
配列が変更されていないことに注目してください (この場合、Arnold は依然としてC++
を専門としています)。しかし、もっと重要なのは、コードが簡単に理解できることです。
配列から重複を削除する
配列から重複を削除するということは、インタビューやホワイトボードの問題としては優れているように思えます (疑わしい場合は、問題に対してハッシュマップを投げることを忘れないでください)。そして、もちろん、それを行うためのカスタム関数をいつでも作成できますが、配列を一意にする必要があるいくつかの異なるシナリオに遭遇した場合はどうすればよいでしょうか?そのために他の関数をいくつか作成することもできます (そして、微妙なバグに遭遇する危険があります)。または、単に Lodash! を使用することもできます。
ユニークな配列の最初の例はかなり些細なものですが、それでも Lodash がもたらす速度と信頼性を表しています。すべてのカスタム ロジックを自分で記述してこれを行うことを想像してみてください。
const _ = require('lodash');
const userIds = [12, 13, 14, 12, 5, 34, 11, 12];
const uniqueUserIds = _.uniq(userIds);
console.log(uniqueUserIds);
// [ 12, 13, 14, 5, 34, 11 ]
最終的な配列はソートされていないことに注意してください。もちろん、これはここでは関係ありません。しかしここで、より複雑なシナリオを想像してみましょう。どこかから取得したユーザーの配列がありますが、そこに一意のユーザーのみが含まれていることを確認したいと考えています。ロダッシュで簡単!
const _ = require('lodash');
const users = [
{ id: 10, name: 'Phil', age: 32 },
{ id: 8, name: 'Jason', age: 44 },
{ id: 11, name: 'Rye', age: 28 },
{ id: 10, name: 'Phil', age: 32 },
];
const uniqueUsers = _.uniqBy(users, 'id');
console.log(uniqueUsers);
/*
[
{ id: 10, name: 'Phil', age: 32 },
{ id: 8, name: 'Jason', age: 44 },
{ id: 11, name: 'Rye', age: 28 }
]
*/
この例では、 uniqBy()
メソッドを使用して、オブジェクトがid
プロパティで一意であるようにすることを Lodash に伝えました。 10 ~ 20 行かかる部分を 1 行で表現し、バグの余地をさらに広げました。
Lodash で独自のものを作成するために利用できるものは他にもたくさんあります。 ドキュメントを参照することをお勧めします。
2 つの配列の違い
和集合や差異などは、集合論に関する退屈な高校の講義で忘れ去られる用語のように聞こえるかもしれませんが、これらは日常の実践で頻繁に登場します。リストがあり、そのリストに別のリストをマージしたり、別のリストと比較してそのリストに固有の要素を見つけたりしたいことはよくあります。このようなシナリオでは、差分関数が最適です。
簡単なシナリオを取り上げて、違いを生み出す旅を始めましょう。システム内のすべてのユーザー ID のリストと、アカウントがアクティブなユーザーのリストを受け取りました。非アクティブな ID はどうやって見つけますか?シンプルですよね?
const _ = require('lodash');
const allUserIds = [1, 3, 4, 2, 10, 22, 11, 8];
const activeUserIds = [1, 4, 22, 11, 8];
const inactiveUserIds = _.difference(allUserIds, activeUserIds);
console.log(inactiveUserIds);
// [ 3, 2, 10 ]
そして、より現実的な設定で起こるように、単純なプリミティブではなくオブジェクトの配列を操作する必要がある場合はどうなるでしょうか?そうですね、Lodash にはこのための優れたdifferenceBy()
メソッドがあります。
const allUsers = [
{ id: 1, name: 'Phil' },
{ id: 2, name: 'John' },
{ id: 3, name: 'Rogg' },
];
const activeUsers = [
{ id: 1, name: 'Phil' },
{ id: 2, name: 'John' },
];
const inactiveUsers = _.differenceBy(allUsers, activeUsers, 'id');
console.log(inactiveUsers);
// [ { id: 3, name: 'Rogg' } ]
きちんとしていますね?!
差分と同様に、Lodash には一般的な集合演算のための他のメソッド (和集合、交差など) があります。
配列の平坦化
配列をフラット化する必要が生じることはよくあります。 1 つのユースケースは、API 応答を受信し、ネストされたオブジェクト/配列の複雑なリストにmap()
とfilter()
のコンボを適用して、たとえばユーザー ID を取り出す必要がある場合です。配列の配列。この状況を表すコード スニペットを次に示します。
const orderData = {
internal: [
{ userId: 1, date: '2021-09-09', amount: 230.0, type: 'prepaid' },
{ userId: 2, date: '2021-07-07', amount: 130.0, type: 'prepaid' },
],
external: [
{ userId: 3, date: '2021-08-08', amount: 30.0, type: 'postpaid' },
{ userId: 4, date: '2021-06-06', amount: 330.0, type: 'postpaid' },
],
};
// find user ids that placed postpaid orders (internal or external)
const postpaidUserIds = [];
for (const [orderType, orders] of Object.entries(orderData)) {
postpaidUserIds.push(orders.filter((order) => order.type === 'postpaid'));
}
console.log(postpaidUserIds);
postPaidUserIds
どのようになっているか推測できますか?ヒント:それは嫌です!
[
[],
[
{ userId: 3, date: '2021-08-08', amount: 30, type: 'postpaid' },
{ userId: 4, date: '2021-06-06', amount: 330, type: 'postpaid' }
]
]
さて、あなたが賢明な人なら、注文オブジェクトを抽出して配列内の行にきれいに配置するカスタム ロジックを書きたくないでしょう。 flatten()
メソッドを使用するだけで、ブドウを楽しむことができます。
const flatUserIds = _.flatten(postpaidUserIds);
console.log(flatUserIds);
/*
[
{ userId: 3, date: '2021-08-08', amount: 30, type: 'postpaid' },
{ userId: 4, date: '2021-06-06', amount: 330, type: 'postpaid' }
]
*/
flatten()
1 レベルしか深くならないことに注意してください。つまり、オブジェクトが 2 つ、3 つ、またはそれ以上のレベルでスタックしている場合、 flatten()
を実行するとがっかりすることになります。このような場合、Lodash にはflattenDeep()
メソッドがありますが、このメソッドを非常に大規模な構造に適用すると処理速度が低下する可能性があることに注意してください (舞台裏で再帰的な操作が行われているため)。
オブジェクト/配列は空ですか?
JavaScript では「偽」の値と型がどのように機能するかによって、空かどうかをチェックするという単純なことが、実存的な恐怖を引き起こすことがあります。
配列が空かどうかをどのように確認しますか? length
が0
かどうかを確認できます。では、オブジェクトが空かどうかをどのように確認するのでしょうか?まあ…ちょっと待ってください!ここで不安な気持ちが始まり、 [] == false
や{} == false
の内容を含む JavaScript の例が頭の中を巡り始めます。機能を提供するというプレッシャーにさらされている場合、このような地雷は最も避けるべきものです。地雷はコードを理解しにくくし、テスト スイートに不確実性をもたらします。
欠損データの処理
現実の世界では、データは私たちの声に耳を傾けます。私たちがどれほどそれを望んでいても、それが合理的で健全であることはめったにありません。典型的な例の 1 つは、API 応答として受信した大規模なデータ構造内で null オブジェクト/配列が欠落していることです。
API 応答として次のオブジェクトを受け取ったとします。
const apiResponse = {
id: 33467,
paymentRefernce: 'AEE3356T68',
// `order` object missing
processedAt: `2021-10-10 00:00:00`,
};
示されているように、通常は API からの応答でorder
オブジェクトを取得しますが、常にそうとは限りません。では、このオブジェクトに依存するコードがある場合はどうなるでしょうか?防御的にコーディングするのも 1 つの方法ですが、 order
オブジェクトのネストの程度によっては、ランタイム エラーを回避したい場合、すぐに非常に見苦しいコードを記述することになります。
if (
apiResponse.order &&
apiResponse.order.payee &&
apiResponse.order.payee.address
) {
console.log(
'The order was sent to the zip code: ' +
apiResponse.order.payee.address.zipCode
);
}
🤢🤢 そうですね、書くのも非常に醜い、読むのも非常に醜い、維持するのも非常に醜い、などです。ありがたいことに、Lodash にはそのような状況に対処する簡単な方法があります。
const zipCode = _.get(apiResponse, 'order.payee.address.zipCode');
console.log('The order was sent to the zip code: ' + zipCode);
// The order was sent to the zip code: undefined
欠落しているものに対してundefined
を取得する代わりに、デフォルト値を提供する素晴らしいオプションもあります。
const zipCode2 = _.get(apiResponse, 'order.payee.address.zipCode', 'NA');
console.log('The order was sent to the zip code: ' + zipCode2);
// The order was sent to the zip code: NA
あなたはどうか知りませんが、 get()
私にとってうれしくて涙を流すものの 1 つです。派手なものではありません。参考にした構文や暗記するオプションはありませんが、それによって軽減できる集団的苦痛の量を見てください。 😇
デバウンス
馴染みのない方のために言っておきますが、デバウンスはフロントエンド開発における一般的なテーマです。その考え方は、アクションをすぐに開始するのではなく、しばらくしてから (通常は数ミリ秒) 開始することが有益な場合があるということです。それはどういう意味ですか?ここに例を示します。
検索バーのある電子商取引 Web サイトを想像してください (最近の Web サイトや Web アプリはすべてそうです)。 UX を向上させるために、ユーザーが検索語に基づいて提案やプレビューを表示するために Enter キーを押す (または、さらに悪いことに、「検索」ボタンを押す) 必要がなくなるようにしたいと考えています。しかし、明らかな答えは少し負荷が高いです。検索バーのonChange()
にイベント リスナーを追加し、キーストロークごとに API 呼び出しを起動すると、バックエンドにとって悪夢が生じることになります。不必要な呼び出しが多すぎます (たとえば、「白いカーペット ブラシ」を検索すると、合計 18 件のリクエストが存在します!)。また、ユーザーの入力が完了していないため、これらのほとんどすべてが無関係になります。
答えはデバウンスにあり、その考え方は、テキストが変更された直後に API 呼び出しを送信しないことです。しばらく待って (たとえば 200 ミリ秒)、その時点までに別のキーストロークがある場合は、前の時間カウントをキャンセルして、再び待機を開始します。その結果、ユーザーが一時停止したとき (考えているため、または作業が完了して何らかの応答を期待しているため) にのみ、API リクエストがバックエンドに送信されます。
私が説明した全体的な戦略は複雑なので、タイマー管理とキャンセルの同期については詳しく説明しません。ただし、Lodash を使用している場合、実際のデバウンス プロセスは非常に簡単です。
const _ = require('lodash');
const axios = require('axios');
// This is a real dogs' API, by the way!
const fetchDogBreeds = () =>
axios
.get('https://dog.ceo/api/breeds/list/all')
.then((res) => console.log(res.data));
const debouncedFetchDogBreeds = _.debounce(fetchDogBreeds, 1000); // after one second
debouncedFetchDogBreeds(); // shows data after some time
setTimeout()
を考えているなら、私も同じ仕事をしたでしょうが、それ以上のものがあります。 Lodash のデバウンスには多くの強力な機能が付属しています。たとえば、デバウンスが無期限にならないようにしたい場合があります。つまり、関数が起動しようとするたびにキーストロークがあったとしても (したがってプロセス全体がキャンセルされます)、API 呼び出しがたとえば 2 秒後に確実に行われるようにしたい場合があります。このため、Lodash debounce()
maxWait
オプションがあります。
const debouncedFetchDogBreeds = _.debounce(fetchDogBreeds, 150, { maxWait: 2000 }); // debounce for 250ms, but send the API request after 2 seconds anyway
さらに詳しく知りたい場合は、公式ドキュメントをチェックしてください。超重要な内容が詰まっています!
配列から値を削除する
あなたはどうか知りませんが、私は配列から項目を削除するコードを書くのが嫌いです。まず、項目のインデックスを取得し、インデックスが実際に有効かどうかを確認し、有効であればsplice()
メソッドを呼び出す必要があります。構文をまったく覚えられないので、常に調べる必要があり、最後には、愚かなバグを忍び込ませてしまったというしつこい気持ちが残ります。
const greetings = ['hello', 'hi', 'hey', 'wave', 'hi'];
_.pull(greetings, 'wave', 'hi');
console.log(greetings);
// [ 'hello', 'hey' ]
次の 2 つのことに注意してください。
- 元の配列はその過程で変更されました。
-
pull()
メソッドは、重複がある場合でもすべてのインスタンスを削除します。
pullAll()
と呼ばれるもう 1 つの関連メソッドがあります。これは 2 番目のパラメーターとして配列を受け取り、複数の項目を一度に簡単に削除できます。確かに、スプレッド演算子でpull()
を使用することはできますが、Lodash は、スプレッド演算子が言語での提案ですらなかった時代に登場したことを思い出してください。
const greetings2 = ['hello', 'hi', 'hey', 'wave', 'hi'];
_.pullAll(greetings2, ['wave', 'hi']);
console.log(greetings2);
// [ 'hello', 'hey' ]
要素の最後のインデックス
JavasScript のネイティブのindexOf()
メソッドは、配列を逆方向からスキャンすることに興味がある場合を除いて、優れています。繰り返しますが、はい、単純に減分ループを作成して要素を見つけることもできますが、もっと洗練されたテクニックを使用してみてはいかがでしょうか?
lastIndexOf()
メソッドを使用した簡単な Lodash ソリューションを次に示します。
const integers = [2, 4, 1, 6, -1, 10, 3, -1, 7];
const index = _.lastIndexOf(integers, -1);
console.log(index); // 7
残念ながら、このメソッドには、複雑なオブジェクトを検索したり、カスタム検索関数を渡したりできるバリアントはありません。
ジップ。解凍してください!
Python で作業したことがない限り、zip/unzip は、JavaScript 開発者としてのキャリア全体を通じて決して気付かなかったり、想像したりすることのないユーティリティです。そしておそらくこれには正当な理由があります。 filter()
などのように zip/unzip が切実に必要になることはほとんどありません。しかし、これはあまり知られていない最高のユーティリティの 1 つであり、場合によっては簡潔なコードを作成するのに役立ちます。 。
見た目に反して、zip/unzip は圧縮とは何の関係もありません。代わりに、同じ長さの配列を、同じ位置にある要素をまとめてパック ( zip()
) して元に戻す ( unzip()
) を持つ単一の配列配列に変換できるグループ化操作です。はい、わかります。言葉で対応しようとすると曖昧になってしまうので、いくつかのコードを見てみましょう。
const animals = ['duck', 'sheep'];
const sizes = ['small', 'large'];
const weight = ['less', 'more'];
const groupedAnimals = _.zip(animals, sizes, weight);
console.log(groupedAnimals);
// [ [ 'duck', 'small', 'less' ], [ 'sheep', 'large', 'more' ] ]
元の 3 つの配列は、2 つの配列のみを含む 1 つの配列に変換されました。そして、これらの新しい配列はそれぞれ、1 つの動物を表し、そのすべてが 1 か所にまとめられています。したがって、インデックス0
はその動物の種類を示し、インデックス1
はそのサイズを示し、インデックス2
は体重を示します。その結果、データの操作が容易になりました。データに必要な操作を適用したら、 unzip()
を使用してデータを再度分割し、元のソースに送り返すことができます。
const animalData = _.unzip(groupedAnimals);
console.log(animalData);
// [ [ 'duck', 'sheep' ], [ 'small', 'large' ], [ 'less', 'more' ] ]
zip/unzip ユーティリティは、一夜にして人生を変えるものではありませんが、ある日、あなたの人生を変えるでしょう。
結論👨🏫
(ブラウザから直接試していただけるよう、この記事で使用されているすべてのソース コードをここに置きます。)
Lodash のドキュメントには、驚くような例や関数がぎっしりと詰まっています。 JS エコシステムでマゾヒズムが増加しているように見える今日の時代において、Lodash は新鮮な空気の息吹のようなものです。このライブラリをプロジェクトで使用することを強くお勧めします。