目次
- 1、概要
- 2、文字列操作
- 3、算術と数値処理
- 4、配列操作
- 5、オブジェクト操作
- 6、関数操作
- 7、汎用ユーティリティ
1、概要
- インストール
// ESモジュール対応でtree-shaking可能
npm install --save lodash-es
// ソースコードの閲覧
// カリー化
// lodash不要プロジェクト: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore
// 配列の高階関数
// JavaScriptの高度な構文
2、文字列操作
- 大文字・小文字変換
/**
* String.toLowerCase/toUpperCase は単純な大文字・小文字変換のみですが、lodashはさらに高度な機能を提供します
*/
_.lowerFirst('文字列');
_.upperFirst('文字列');
// 最初の文字を大文字に、それ以外を小文字に
_.capitalize('文字列');
- 命名規則の変換
/**
* プログラミングで一般的な単語複合の命名規則には以下のようなものがあります:
* スネークケース(snake case): 単語をアンダースコアで接続、例: foo_bar
* ケバブケース(kebab case): 単語をハイフンで接続、例: foo-bar
* キャメルケース(camel case): 2つ目の単語から各単語の先頭を大文字、例: fooBar
* パスカルケース(pascal case): 各単語の先頭を大文字、例: FooBar
* パスカルケースを除く3つの形式には対応する変換関数があります:
*/
_.snakeCase('文字列');
_.kebabCase('文字列');
_.camelCase('文字列');
// upperFirstとcamelCaseを組み合わせてpascalCaseを実装
const toPascalCase = (str) => _.upperFirst(_.camelCase(str));
// 使用例
_.snakeCase('fooBar'); // 'foo_bar'
_.camelCase('Foo Bar'); // 'fooBar'
_.kebabCase('__FOO_BAR__'); // 'foo-bar'
/**
* さらに、あまり使用されない全大文字と全小文字の形式(スペース区切り)もあり、_.toLower/toUpperとの違いは区切り文字を認識して変換する点です
*/
_.lowerCase('文字列');
_.upperCase('文字列');
// 使用例
_.lowerCase('--Foo-Bar--'); // 'foo bar'
_.upperCase('fooBar'); // 'FOO BAR'
3、算術と数値処理
/**
* 算術演算:
*/
// 合計値を計算
_.calculateSum(配列);
// 平均値を計算
_.calculateAverage(配列);
/**
* 数値操作の一般的な関数:
*/
// [lower, upper]間のランダムな数値を返す
// lowerかupperに浮動小数点数がある場合、またはfloatingがtrueの場合は浮動小数点数を返し、それ以外は整数を返す
_.generateRandomNumber(lower=0, upper=1, floating)
// 範囲配列を生成
_.createRangeArray([start=0,] end, step=1)
// 数値を指定範囲内にクランプ
_.limitValue(number, [lower=0,] upper)
// 使用例
_.limitValue(-10, -5, 5); // -5
_.limitValue(10, -5, 5); // 5
4、配列操作
- 集合演算
// 積集合 intersection
_.findIntersection(...arrays);
_.findIntersectionWith(...arrays [, comparator]);
_.findIntersectionBy(...arrays [, iteratee]);
// 和集合
_.findUnion(...arrays);
_.findUnionWith(...arrays [, comparator]);
_.findUnionBy(...arrays [, iteratee]);
// 差集合、A - B は集合Aに属するが集合Bに属さない要素の集合
_.findDifference(array, ...operands);
_.findDifferenceWith(array, ...operands [, comparator]);
_.findDifferenceBy(array, ...operands [, iteratee]);
/**
* これら3つの関数群は元の配列を変更せず、新しい配列を結果として返します。積集合と和集合の演算結果には重複要素は含まれません。差集合は最初の配列に依存します。
* 関数名には一定の規則があり、積集合を例に説明します:
* intersection: 通常の演算を実行し、浅い比較で要素の等価性を判断します。
* intersectionWith: 比較関数を呼び出して要素を比較し、比較方法をカスタマイズできます。
* intersectionBy: 各要素をまずiteratee関数で処理し、変換後の配列で比較演算を実行し、最終的に変換前の最初の要素を結果とします。
*/
_.findIntersection([2, 1, 1], [2, 3], [2, 4]);
// => [2]
const dataObjects = [
{ x: 1, y: 2 },
{ x: 2, y: 1 },
];
const otherObjects = [
{ x: 1, y: 1 },
{ x: 1, y: 2 },
];
_.findIntersectionWith(dataObjects, otherObjects, _.isEqual);
// => [{ 'x': 1, 'y': 2 }], 結果はdataObjectsの要素を参照
_.findIntersectionBy([2.1, 1.2], [2.3, 3.4], [3.2, 2.4], Math.floor);
// => [2.1]
- 分割/分割/グループ化
/**
* 分割(chunk)は配列のn個ごとにグループ(一片)に分けることを指します。割り切れない場合、最後の要素は単独の一片になります。
*/
_.divideIntoChunks(array [, size=1])
// 使用例
_.divideIntoChunks(['a', 'b', 'c', 'd','e'], 2);
// => [["a", "b"], ["c", "d"], ["e"]]
/**
* 分割(partition)は述語関数で各要素を反復処理し、述語のtrueとfalseに基づいて要素を2つのグループに分けます。
*/
_.splitByCondition(collection [, predicate])
// 使用例
_.splitByCondition([4,5,6,7], num => num > 5)
// =>[[6, 7], [4, 5]]
/**
* グループ化(group)は関数で各要素を反復処理し、結果をその要素のグループのキーとして使用し、同じキーの要素を同じグループにまとめます。
*/
_.organizeByGroup(collection [, iteratee])
// 使用例
_.organizeByGroup([6.1, 4.2, 6.3], Math.floor);
// => { '4': [4.2], '6': [6.1, 6.3] }
- 有序配列の検索/重複除去
/**
* 配列がソートされている場合、検索と重複除去に二分法を使用して複雑度を低下させることができます。Lodashはソート済み(昇順)配列向けの操作もいくつか提供しています。
* sortedIndex / sortedLastIndex は基本的な数値配列と文字列配列で使用できます:
*/
// 要素を挿入しても配列がソートされたままになる最初のインデックス位置を返す
_.findInsertIndex(array, value);
// sortedIndexに似ていますが、順序を維持できる最後のインデックス位置を返します
_.findLastInsertIndex(array, value);
// 使用例
_.findInsertIndex([1, 20, 20, 100, 500], 20); // 1
_.findLastInsertIndex([1, 20, 20, 100, 500], 20); // 3
/**
* 上記2つの関数は数値と文字列の配列でのみ使用できます。オブジェクト配列の場合、要素間の並べ替え基準を示す関数を使用できます:
*/
* iterateeで変換した結果でソート
_.findInsertIndexBy(array, value [, iteratee])
_.findLastInsertIndexBy(array, value [, iteratee])
// 使用例
_.findInsertIndexBy([{ 'x': 4 }, { 'x': 5 }], { 'x': 4 }, function(o) { return o.x; });
// => 0
/**
* 注意点として、sortedIndex/sortedLastIndexは直接要素の検索には使用できません。例えば、上記でインデックス3が返されますが、array[3]は100ではなく20です。
* 有序配列の検索にはsortedIndexOf/sortedLastIndexOfを使用し、これはindexOf/lastIndexOfと同じ機能ですが、二分検索を使用します。
*/
_.findIndexOf(array, value);
_.findLastIndexOf(array, value);
// 使用例
_.findIndexOf([4, 5, 5, 5, 6], 5); //1
/**
* sortedUniq/sortedUniqByはソート済み配列の重複を除去します。
*/
_.removeDuplicates(array)
_.removeDuplicatesBy(array [, iteratee])
// 使用例
_.removeDuplicatesBy([1.1, 1.2, 2.3, 2.4], Math.floor);
// => [1.1, 2.3]
- 要素操作:サンプリング/シャッフル/カウント
// ランダムに1要素を返す
_.getRandomElement(collection)
// ランダムにn要素を返す
_.getRandomElements(collection, [n=1])
// 配列をシャッフル
_.shuffleArray(collection)
// カウント
_.countElements(collection [, iteratee])
_.countElements([6.1, 4.2, 6.3], Math.floor);
// => { '4': 1, '6': 2 }
5、オブジェクト操作
- オブジェクト変換
/**
* 開発では既存のオブジェクトを改造して望ましい構造を得ることがよくあります。Lodashには多くの関数が役立ちます:
*/
// クローン
_.createClone(value);
_.createCloneWith(value, customizer);
// ネイティブの深いコピー: const clone = structuredClone(original)
_.createDeepClone(value);
_.createDeepCloneWith(value, customizer);
// Object.assignと同様、sourcesオブジェクトの独自プロパティをobjectに割り当て
_.assignProperties(object, ...sources);
// 変換後に割り当て
_.assignPropertiesWith(object, sources, customizer);
// _.assignに似ていますが、継承されたプロパティも割り当てます
_.assignInherited(object, ...sources);
_.assignInheritedWith(object, ...sources, customizer);
// objectに値が存在しない場合のみ割り当て、デフォルト値のマージによく使用
_.setDefaultValues(object, ...sources);
// _.defaultは多層オブジェクトには使用できず、_.defaultsDeepを使用する必要があります
_.setDefaultValuesDeep(object, ...sources);
// オブジェクトをマージ、_.assignに似ていますが、オブジェクトは再帰的に深く、配列は連結されます
_.combineObjects(object, ...sources);
_.combineObjectsWith(object, ...sources);
/**
* 上記の関数はすべてobjectパラメータを直接変更します。
* これとは異なり、pickとomit操作は新しいオブジェクトを返し、パラメータを変更しません:
*/
// オブジェクトから対応するパスの値を取り出し、新しいオブジェクトを生成
_.extractProperties(object, [paths]);
// 述語関数でプロパティを取るかどうかを決定、predicate(value,key)
_.extractPropertiesBy(object, predicate);
// 指定されたプロパティを削除し、残りを新しいオブジェクトに結合、pickより性能が劣る
_.removeProperties(object, [paths]);
_.removePropertiesBy(object, predicate);
// 使用例
_.extractProperties({ a: 1, b: "2", c: 3 }, ["a", "c"]);
// => { 'a': 1, 'c': 3 }
_.extractPropertiesBy({ a: 1, b: "2", c: 3 }, _.isNumber);
// => { 'a': 1, 'c': 3 }
_.removeProperties({ a: 1, b: "2", c: 3 }, ["a", "c"]);
// => { 'b': '2' }
/**
* さらに、オブジェクトは配列のようにmap操作もできます:
*/
// iteratee(value,key,obj)の結果を新しいオブジェクトのkeyとして使用
_.transformKeys(object, iteratee);
// iteratee(value,key,obj)の結果を新しいオブジェクトのvalueとして使用
_.transformValues(object, iteratee);
// 使用例
_.transformKeys({ a: 1, b: 2 }, function (value, key) {
return key + value;
});
// => { 'a1': 1, 'b2': 2 }
- オブジェクトの反復処理
// 独自プロパティを反復処理、for...inとhasOwnPropertyの判断に似ています
_.iterateOwnProperties(object, iteratee);
// 条件に一致するキーを検索、配列のfindIndexに似ています
_.locateKey(object, iteratee);
_.locateLastKey(object, iteratee);
// オブジェクト内の関数プロパティを検索
_.findFunctionProperties(object);
_.findFunctionPropertiesIn(object);
- 安全なget/set
/**
* JavaScriptでは、特定のパスの値を読み取ったり設定したりするのは安全ではありません:
*/
const dataObject={a:1};
const value=dataObject.b.someKey; // TypeError: Cannot read properties of undefined
dataObject.c.someKey=newValue; // TypeError: Cannot set properties of undefined
/**
* Lodashはより安全なgetとset操作を提供しています:
*/
// pathに対応する値が存在しない場合、エラーではなくundefinedを返します
_.getSafely(object, path [, defaultValue]);
// 1層ずつsetし、エラーになりません
_.setSafely(object, path, value);
// 現在のpathの値に基づき、updaterが返した値に更新、updater(value)=>newValue
_.updateSafely(object, path, updater)
/**
* 最新のオプショナルチェーン "?." 構文はget関数に取って代わることができますが、set操作には依然として十分なネイティブサポートがありません。
*/
6、関数操作
- 関数実行の制御
/**
* 時には特定の条件下でのみ関数を実行したい場合があります。例えば一般的なデバウンスとスロットル:
* デバウンス(debounce): 関数が呼び出されたとき、実際の操作(内部関数)の実行を待ちます。この期間中に再度関数が呼び出された場合、この呼び出しは実行せず、新しい呼び出しで待ちを再開します。
* スロットル(throttle): 一定期間内に複数回関数を呼び出しても、実際の操作は1回のみ実行されます。
*/
_.createDebounce(func [, wait=0] [, options={}])
_.createThrottle(func [, wait=0] [, options={}])
// 使用例
// 停止してから再レイアウト
window.addEventListener('resize', _.createDebounce(calculateLayout, 150));
// 位置の更新を継続しますが、150msごとに更新し、カクつきを防ぎます
window.addEventListener('resize', _.createThrottle(updatePosition, 150));
/**
* 呼び出し回数に基づいて操作を実行するかどうかを制御することもできます。
*/
// 関数を1回のみ呼び出す
_.executeOnce(func);
// 最初のn回の呼び出みのみ
_.executeBefore(func, n);
// n回の呼び出し以降のみ
_.executeAfter(func, n);
/**
// 遅延実行:
*/
// 現在の呼び出しスタックがクリアされた後に実行
_.scheduleExecution(func, ...args);
// waitミリ秒待ってから実行、setTimeoutと同じ
_.postponeExecution(func, wait, ...args);
/**
* _.memorizeは関数の結果をキャッシュし、重複計算を避け、一般的なパフォーマンス最適化手法です。
*/
// キャッシュキーを計算するresolver、キーが同じ場合キャッシュを使用。デフォルトは最初のパラメータをキーとして使用
_.createMemoize(func [, resolver])
- 関数パラメータの変換
// カリー化
_.applyCurry(func, (arity = func.length));
// 一部のパラメータをバインドしますが、thisはバインドしません
_.applyPartial(func, ...args);
// 最初のn個のパラメータのみを受け取り、追加のパラメータは無視
_.limitParameters(func, n);
// 最初のパラメータのみを受け取り、_.ary(func,1)と同じ
_.useSingleParameter(func);
7、汎用ユーティリティ
/**
* パイプライン:
*/
_.createPipeline([funcs]);
const toPascalCase = _.createPipeline(_.upperFirst, _.camelCase);
/**
* 一意のIDを生成:
*/
_.generateUniqueId((prefix = ""));