lodashクイズ
Array 取得系
オンラインエディタでためせる。
Q1. 配列の最初の値を取得する
const data = [1, 2, 3];
const message = _.???(data);
console.log(message)
// [1]
答え
head(array)
エイリアス: first
Q2. 配列の最初以外の値を取得する
const data = [1, 2, 3];
const message = _.???(data);
console.log(message)
// [2, 3]
答え
Q3. 配列の最後の値を取得する
const data = [1, 2, 3];
const message = _.???(data);
console.log(message)
// [3]
答え
Q4. 配列の最後以外の値を取得する
const data = [1, 2, 3];
const message = _.???(data);
console.log(message)
// [1, 2]
Q5. 左から3個分の値を取得する
const data = [1, 2, 3, 4, 5];
const message = _.???(data, 3);
console.log(message)
// [1, 2, 3]
答え
Q6. 右から3個分の値を取得する
const data = [1, 2, 3, 4, 5];
const message = _.???(data, 3);
console.log(message)
// [3, 4, 5]
Q7. 右から2番目の値を取得する
const data = [1, 2, 3, 4, 5];
const message = _.???(data, -2);
console.log(message)
// [4]
答え
Q8. 条件を満たす限り左から順に値を取得する
const data = [1, 2, 3, 4, 5, 4, 3, 2, 1];
const predicate = (val: number) => val < 3;
const message = _.???(data, predicate);
console.log(message)
// [1, 2]
Q9. 条件を満たす値を全て取得する
const data = [1, 2, 3, 4, 5, 4, 3, 2, 1];
const predicate = (val: number) => val < 3;
const message = _.???(data, predicate);
console.log(message)
// [1, 2, 2, 1]
Q10. ランダムに要素を取得する
const data = [1, 2, 3, 4, 5];
const message = _.???(data);
console.log(message)
// ???
Q11. 左から3個目から4個目までの要素を取得する。
const data = [1, 2, 3, 4, 5];
const message = _.???(data, 2, 4);
console.log(message)
// [3, 4]
Array 除外系
Q1. ある値を除外する
const data = [2, 1, 2, 3];
const message = _.???(data, 1, 2);
console.log(message)
// [3]
Q2. 左から2個の値を除外する
const data = [2, 1, 2, 3];
const message = _.???(data, 2);
console.log(message)
// [2, 3]
答え
Q2. 右から2個の値を除外する
const data = [2, 1, 2, 3];
const message = _.???(data, 2);
console.log(message)
// [2, 1]
Q3. falseyな値を排除する
const data = [0, 1, false, 2, '', 3];
const message = _.???(data);
console.log(message)
// [1, 2, 3]
Q4. 条件を満たす値を全て除外する
const data = [1, 2, 3, 4, 5, 4, 3, 2, 1];
const predicate = (val: number) => val > 3;
const message = _.???(data, predicate);
console.log(message)
// [1, 2, 3, 3, 2, 1]
Array 2つ(以上)の配列からある配列を1つ作る
Q1. 単純な連結
const data1 = [2, 1];
const data2 = [2, 3];
const message = _.???(data1, data2);
console.log(message)
// [2, 1, 2, 3]
Q2. 連結させて重複を取り除く
const data1 = [2, 1];
const data2 = [2, 3];
const message = _.???(data1, data2);
console.log(message)
// [2, 1, 3]
答え
Q3. 1つめの配列にあって2つめの配列にないものを取得する
const data1 = [2, 1];
const data2 = [2, 3];
const message = _.???(data1, data2);
console.log(message)
// [1]
Q4. 2つの配列に共通する要素を取得する
const data1 = [2, 1];
const data2 = [2, 3];
const message = _.???(data1, data2);
console.log(message)
// [2]
Q5. 2つの配列に共通しない要素を取得する
const data1 = [2, 1];
const data2 = [2, 3];
const message = _.???(data1, data2);
console.log(message)
// [1, 3]
答え
Array その他
Q1. ユニークな値を取得する
const data = [0, 1, 0];
const message = _.???(data);
console.log(message)
// [1]
答え
Q2. ソートされた配列からユニークな値をする
const data = [0, 0, 1]];
const message = _.???(data);
console.log(message)
// [1]
Q3. 逆順にする(破壊的)
const data = [1, 2, 3];
const message = _.???(data);
console.log(message)
// [3, 2, 1]
cosole.log(data)
// [3, 2, 1]
答え
Q4. シャッフルする
const data = [1, 2, 3, 4];
const message = _.???(data);
console.log(message)
// [4, 1, 3, 2]
Q5. シンプルな昇順ソート
const data = [
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 36 },
{ 'user': 'fred', 'age': 40 },
{ 'user': 'barney', 'age': 34 }
];
const message1 = _.???(data, [function(o) { return o.user; }]);
console.log(message1)
// [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
const message2 = _.???(data, ['user', 'age']);
console.log(message2)
// [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]
Q6. 降順もあるソート
const data = [
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 36 },
{ 'user': 'fred', 'age': 40 },
{ 'user': 'barney', 'age': 34 }
];
const message = _.???(data, ['user', 'age'], ['asc', 'desc']);
console.log(message)
// [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
Array タプル操作
Q1. keyとvalueのタプル配列からオブジェクトを作成する
const data = [
['a', 1],
['b', 2]
];
const message = _.???(data);
console.log(message)
// { 'a': 1, 'b': 2 }
Q2. key配列とvalue配列を受け取りオブジェクトを作成する
const keys = ['a', 'b'];
const values = [1, 2];
const message = _.???(keys, values);
console.log(message)
// { 'a': 1, 'b': 2 }
Q3. 各配列の要素をインデックスごとにグループ化する
const data1 = ['a', 'b'];
const data2 = [1, 2];
const data3 = [true, false];
const message = _.???(data1, data2, data3);
console.log(message)
// [['a', 1, true], ['b', 2, false]]
答え
Q4. 各配列の要素をインデックスごとにグループ化する
const data = [['a', 'b'], [1, 2], [true, false]];
const message = _.???(data);
console.log(message)
// [['a', 1, true], ['b', 2, false]]
答え
Array&Collection 探す系
Q1. 条件に一致する要素をみつけ、最初の1つを取得
const data = [
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'fred', 'age': 40, 'active': false },
{ 'user': 'pebbles', 'age': 1, 'active': true }
];
const predicate = (o) => o.age < 40;
const message = _.???(data, predicate);
console.log(message);
// { 'user': 'barney', 'age': 36, 'active': true }
答え
その他の使い方
第2引数には、オブジェクトや配列やkey変数を渡すような使い方もできる。
var users = [
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'fred', 'age': 40, 'active': false },
{ 'user': 'pebbles', 'age': 1, 'active': true }
];
_.find(users, function(o) { return o.age < 40; });
// => object for 'barney'
// The `_.matches` iteratee shorthand.
_.find(users, { 'age': 1, 'active': true });
// => object for 'pebbles'
// The `_.matchesProperty` iteratee shorthand.
_.find(users, ['active', false]);
// => object for 'fred'
// The `_.property` iteratee shorthand.
_.find(users, 'active');
// => object for 'barney'
Q2. 条件に一致する要素をみつけ、最後の1つを取得
const data = [
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'fred', 'age': 40, 'active': false },
{ 'user': 'pebbles', 'age': 1, 'active': true }
];
const predicate = (o) => o.age < 40;
const message = _.???(data, predicate);
console.log(message);
// { 'user': 'pebbles', 'age': 1, 'active': true }
Q3. Q1において要素の変わりにインデックスを取得したい
Q4. Q2において要素の変わりにインデックスを取得したい
Q5. その値に一致する要素を、2番目以降のインデックスがらみつけ、そのインデックスを取得する
const data = [1, 2, 1, 2]
const message = _.???(data, 2, 2);
console.log(message);
// 3
Q6. 右から左にさがす。その値に一致する要素を、右から2番目以降のインデックスがらみつけ、そのインデックスを取得する
const data = [1, 2, 1, 2]
const message = _.???(data, 2, 2);
console.log(message);
// 1
Q7. 配列がソートされているとする。第2引数の値が、第1引数のインデックスで何番目にくるのかを知りたい(二分探索)
const data = [30, 50]
const message = _.???(data, 40);
console.log(message);
// 1
Array&Collection 配列の値から1つの値を導き出す畳み込み系
Q1. 文字列の配列をカンマ区切りで1つの文字列に結合する
const data = ['a', 'b', 'c']
const message = _.???(data, ',');
console.log(message);
// 'a,b,c'
答え
Q2. ある条件を適応させたとき重複しているものが何個あるのか知りたい
const data = [6.1, 4.2, 6.3];
const message = _.???(data, Math.floor);
console.log(message);
// { '4': 1, '6': 2 }
Q3. reduce
Q4. size
Array&Collection 再構築系
chunk: 1つの配列を複数に分割する
partition: 1つの配列を2つに分割する
map: 反復処理で新しい配列を作成
flatMap: .flat(.map())
invokeMap: 反復処理でそれぞれのオブジェクトがもつメソッドを呼び覚ます
groupBy: 指定されたkeyおよび条件ごとにグルーピング
keyBy: keyのvalueごとにグルーピング
Array 効率重視の破壊系
fill
pull
pullAll
pullAt
remove
真偽値を返す関数(predicate)
includes
some
every
オブジェクト 取得系
keys
findKey
values
get: pathで深いネストの値まで取得できる。デフォルト値を設定可能。
at: getの複数パス(path)対応
has: 指定されたプロパティ(key)をもつかどうか
pick: オブジェクトから、指定されたkeyのみをもつ新しいオブジェクトを作る
omit: オブジェクトから、指定されたkeyを除外した新しいオブジェクトを作る
toPairs: fromPairsの逆。keyとvalueの配列を作る。
オブジェクトの反復処理
mapValues
mapKeys
forIn: オブジェクトのすべての列挙可能なプロパティに対してループを実行。プロトタイプチェーンを遡ることもある。
forOwn: オブジェクトのすべての列挙可能なプロパティに対してループを実行。プロトタイプチェーンは遡らない。
transform: reduceのオブジェクト版
オブジェクトの合成系
create: プロトタイプを継承してオブジェクトの作成
assign
merge
defaults: オプションとかデフォルトの値で上書きしたいときにつかえそう
オブジェクト更新系
破壊系もあるが、fpモジュールを有効にすれば非破壊として利用できる
update
set
unset
invert: keyとvalueを逆転させたオブジェクトを作る
便利系関数
before: n回目に呼ばれる前に実行
import _ from 'lodash'
const files = ['hoge', 'fuga'];
const onAllFileSaved = _.before(files.length, () => {
console.log('All files are saved!')
})
files.forEach((file) => {
console.log(`${file} saved!`);
onAllFileSaved()
})
after: n回目に呼ばれた後に実行
import _ from 'lodash'
const files = ['hoge', 'fuga'];
const onAllFileSaved = _.after(files.length, () => {
console.log('All files are saved!')
})
files.forEach((file) => {
console.log(`${file} saved!`);
onAllFileSaved()
})
debounce: 間引くやつ
throttle: 間引くやつ
delay: sleepてきな
once: 一度だけの呼び出しを保証。2度めは1度目のキャッシュを返す
ary: 引数の数をn個に固定
unary: 引数の数を1つに固定
wrap: <p> なにか </p> みたいなのが作れる
関数合成系
bind: オブジェクトへのバインド操作
bindKey
curry: カリー化
curryRight
partial: 部分適用
partialRight
flow: 関数の連結。引数を最初の関数に渡し、その結果を2番めの関数に渡し...と続けていく。
function square(n) {
return n * n;
}
var addSquare = _.flow([_.add, square]);
addSquare(1, 2);
// => 9
over: 引数の値を複数の関数に渡して、それぞれの結果を返す。Promise.all()的なイメージ
overSome: _.every(_.over([predicate])
overSome: _.some(_.over([predicate])
overArgs: 引数の変換
flowRight: flowの右から適用版
flip: 引数の入れ替え
rearg: 引数の変換
negate: 述語関数(predicate)の結果を逆転
spread: 意義がよくわからない
rest: 第2引数以降がすべて、restの第2引数に入る
wrap: 既存関数の拡張にも使える。
便利関数2
attempt: エラーをthrowせずに引数に返す
var elements = _.attempt(function(selector) {
return document.querySelectorAll(selector);
}, '>_>');
if (_.isError(elements)) {
console.log(elements);
// SyntaxError: Failed to execute 'querySelectorAll' on 'Document': '>_>' is not a valid selector.
elements = [];
}
cond: マッチ条件関数とコールバック関数のタプルの配列を順番に実行していって、条件にマッチしたらそのコールバック関数を実行する。
var func = _.cond([
[_.matches({ 'a': 1 }), _.constant('matches A')],
[_.conforms({ 'b': _.isNumber }), _.constant('matches B')],
[_.stubTrue, _.constant('no match')]
]);
func({ 'a': 1, 'b': 2 });
// => 'matches A'
func({ 'a': 0, 'b': 1 });
// => 'matches B'
func({ 'a': '1', 'b': '2' });
// => 'no match'
noop: () => {}
つまり何もしないデフォルトでつかうやつ。no operation。
conforms: predicate関数でget
を使って条件分岐しているのであれば、conformsを使えばより端的に書ける。"Conform"という言葉は、一般的に「適合する」や「従う」といった意味を持つ。
const users = [
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'fred', 'age': 40, 'active': false }
];
const isBarney = _.conforms({ 'user': (value) => value === 'barney', 'active': (value) => value === true });
console.log(_.filter(users, isBarney));
// 結果: [{ 'user': 'barney', 'age': 36, 'active': true }]
matches: オブジェクトのプロパティに対して浅い比較をおこなう。conformよりも自由度は低い。
const users = [
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'fred', 'age': 40, 'active': false }
];
const isBarney = _.matches({ 'user': 'barney', 'active': true });
console.log(_.filter(users, isBarney));
// 結果: [{ 'user': 'barney', 'age': 36, 'active': true }]
慣用的な表現
〇〇By
例: _.intersectionBy(array1, array2, [iteratee])
第2引数などに条件関数を渡してより詳細な動作を行う系。
iterateeは、要素の比較に使用される値を抽出するための関数。この関数により、要素を比較する前に変換や抽出を行うことができる。
const array1 = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }];
const array2 = [{ 'x': 2 }, { 'x': 3 }, { 'x': 4 }];
// 'x' は `_.property` iteratee の省略
const result = _.intersectionBy(array1, array2, 'x');
// 結果: [{ 'x': 2 }, { 'x': 3 }]
〇〇With
例: _.intersectionWith(array1, array2, [comparator])
条件関数としてcomparatorを渡すのが〇〇Byと違う。comparatorは2つの引数を比較し、その大小や優劣で1, 0, -1を返す関数。基本的にはisEqual
を使うことが多い?
〇〇In
オブジェクトのプロトタイプチェーンの継承先まで見ますよ系
〇〇While
終了条件を満たすまでループ実行しますよ系
〇〇Right
配列の走査を右から行いますよ系
〇〇Of
第1引数と第2引数の関係性が逆転する。
キーワード整理 iteratee、 predicate、comparator
ChatGPTによる生成
iteratee
iterateeはLodashにおいて重要な概念で、主にコレクションをイテレート(反復処理)する際に適用される関数を指します。iterateeは、.map、.filter、_.forEachなどのメソッドに渡され、各要素に対する処理を定義します。
Lodashのiterateeの重要性は以下の点にあります:
- 柔軟性と拡張性:
iterateeを利用することで、非常に柔軟で拡張性のあるイテレーション処理を実現できます。iterateeは通常、コレクションの各要素に対して適用される関数であり、この関数の中で要素ごとの処理が定義されます。これにより、さまざまな要件に応じて処理をカスタマイズできます。
- ショートハンドの提供:
Lodashのiterateeは、簡潔でわかりやすいコードを書くためのショートハンドを提供します。例えば、プロパティの取得や比較など、一般的な操作に対するシンプルな関数を提供することができます。
// ショートハンドを利用した例
const users = [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }];
const getUser = _.iteratee('user');
console.log(_.map(users, getUser));
// 結果: ['barney', 'fred']
- 遅延評価のサポート:
iterateeは遅延評価をサポートし、関数が実際に必要なときに評価されます。これにより、計算のオーバーヘッドを削減し、効率的な処理を実現できます。
const numbers = [1, 2, 3, 4, 5];
const squared = _.map(numbers, (n) => n * n);
console.log(squared);
// 結果: [1, 4, 9, 16, 25]
iterateeの使い方によって、コードをシンプルかつ効果的に保つことができます。適切に定義されたiterateeは、コレクションの要素ごとの処理を容易にカスタマイズし、柔軟かつ効率的なコードを記述するのに役立ちます。
predicate
predicateは、真偽値を返す関数を指すことが多いです。
一般的に、predicateはコレクションの各要素に適用され、その結果に基づいて要素が残るか削除されるかを判断します。.filterや.removeメソッドで使われることがあります。
const array = [1, 2, 3, 4, 5];
// 偶数の要素だけを残すpredicate
const filteredArray = _.filter(array, (num) => num % 2 === 0);
// 結果: [2, 4]
comparator
- comparatorは、比較関数を指定するための引数です。
- 一般的に、comparatorは2つの要素を受け取り、それらを比較してどちらが優れているかを判定します。比較結果に基づいてソートやフィルタリングなどの操作が行われます。
- 例えば、.sortByや.intersectionWithメソッドで使われます。
const array = [{ 'x': 1 }, { 'x': 3 }, { 'x': 2 }];
// 'x'プロパティの値でソートするcomparator
const sortedArray = _.sortBy(array, (obj) => obj.x);
// 結果: [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }]
iteratee深掘り
この記事がわかりやすかった。
over vs overArgs vs flow vs wrap
over
overは、複数の関数を受け取り、これらの関数を引数に適用し、結果をまとめて配列として返す関数を生成します。これは、一連の関数を同じ引数に適用して、その結果をまとめて取得する場合に便利です。
const _ = require('lodash');
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const combinedFunction = _.over([add, multiply]);
const result = combinedFunction(3, 4);
console.log(result); // [7, 12]
overArgs
overArgsは、指定された関数を複数の引数に対して適用する新しい関数を生成します。各引数に対して別々の関数を適用する場合に使用されます。
const _ = require('lodash');
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const combinedFunction = _.overArgs((x, y) => [x, y], [add, multiply]);
const result = combinedFunction(3, 4);
console.log(result); // [7, 12]
flow
flowは、複数の関数を合成して新しい関数を生成します。合成された関数は、引数を最初の関数に渡し、その結果を次の関数に渡していくように実行されます。合成された関数は最後の関数の結果を返します。
const _ = require('lodash');
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const combinedFunction = _.flow([add, multiply]);
const result = combinedFunction(3, 4);
console.log(result); // (3 + 4) * 2 = 14
wrap
wrapは、指定された関数をラップして新しい関数を生成します。生成された関数は、指定された関数の前後に追加の処理を挿入することができます。
const _ = require('lodash');
const wrappedFunction = _.wrap((value) => `Value is: ${value}`, (func, value) => func(value));
const result = wrappedFunction(42);
console.log(result); // 'Value is: 42'
over系の関数の違い
Lodashの over 系の関数は、複数の関数を組み合わせて新しい関数を生成するものです。以下は主な over 系関数とそれらの違いです。
over
overは複数の関数を受け取り、これらの関数を引数に適用し、結果を配列としてまとめて返す関数を生成します。
const _ = require('lodash');
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const combinedFunction = _.over([add, multiply]);
const result = combinedFunction(3, 4);
console.log(result); // [7, 12]
overEvery
overEveryは複数の述語関数(真偽値を返す関数)を受け取り、これらの関数を引数に適用し、すべてが真であるかどうかを判定する新しい述語関数を生成します。
const _ = require('lodash');
const isEven = (n) => n % 2 === 0;
const isPositive = (n) => n > 0;
const combinedPredicate = _.overEvery([isEven, isPositive]);
console.log(combinedPredicate(4)); // true
console.log(combinedPredicate(-2)); // false
overSome
overSomeは複数の述語関数を受け取り、これらの関数を引数に適用し、少なくとも一つが真であるかどうかを判定する新しい述語関数を生成します。
const _ = require('lodash');
const isEven = (n) => n % 2 === 0;
const isPositive = (n) => n > 0;
const combinedPredicate = _.overSome([isEven, isPositive]);
console.log(combinedPredicate(4)); // true
console.log(combinedPredicate(-2)); // true
console.log(combinedPredicate(-3)); // false
overEveryとoverSomeは_.every(_.over(data))
と_.some(_.over(data))
のイメージかな?