JavaScript の map などのコールバックで 2 つめ以降がいらない
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ 引数取る数制限したい
◆ .map(parseInt) とかしたい
◆ .map(parseInt) とかしたい
JavaScript で map や forEach を使うと コールバック関数は
という形で呼び出されます
reduce だと最初の値のところが 集計値と各要素の値の 2 つになって合計 4 つになりますが 基本どれもインデックスと配列自身も渡されます
他の言語と違って引数の数があってる必要はないのでいらないなら受け取らないことができます
だけでもいいです
C# だと引数があってないとエラーです
なので一見便利なだけで困らないのですが
これを 略して
と書くとちゃんと動きません
2 つめの引数が基数なのでインデックスの値のせいで 2 進数や 3 進数として解釈してしまっています
他にも String.fromCharCode などがあります
ES6 のアロー関数は短くかけるのでそこまで負担はないのですが こういうのは関数そのまま渡したいです
どうにかしたいのですが map と同じ動きでコールバックの呼び出しだけ違う関数を用意するのが一番に思えます
でも 全部の関数分作るのって大変だし map や forEach はそのままでどうにかしたいな と思って一応工夫してやってみました
parseInt 的な関数はわかりやすくするため この console.log する関数にします
まず普通に forEach すると
pass メソッドを作ります
これをつかって 引数をひとつだけにします
one のところは本来
のように引数を変換して配列にするものです
最初の引数だけ必要なので省略した書き方にしてます
引数全部そのまま通すなら
こうです
悪いところは pass も one も作らないとダメなので準備がめんどうです
さらに 毎回書く部分も ES6 かけるなら e => fn(e) のほうが短いです
一応 one のところに他のものをいれることもできますが あまり使いみちもないです
今回は プロトタイプはそのままで別に関数を作ってます
関数名を短くすれば アロー関数よりはタイプしやすいです
これの使い方は
という感じ
これを forEach に使います
引数で渡された複数の関数を順番に適用させていく関数を返すものですが 引数ひとつだけなら その関数を実行するだけの関数です
連結する関係で引数は 1 つになっているので 2 つめ以降は無視されて 一つ目だけ 渡されるのでちょうど良くなります
こういう便利関数は作ってることも多いのでよさそうです
ただ reduce みたいな 2 つという例外は対応できないですけど
基本 1 つしか使わないのは上書きしてしまうとか
callback(値, インデックス, 配列自身)
という形で呼び出されます
reduce だと最初の値のところが 集計値と各要素の値の 2 つになって合計 4 つになりますが 基本どれもインデックスと配列自身も渡されます
他の言語と違って引数の数があってる必要はないのでいらないなら受け取らないことができます
;[1].forEach((e, i, s) => console.log(e))
じゃなくて;[1].forEach(e => console.log(e))
だけでもいいです
C# だと引数があってないとエラーです
なので一見便利なだけで困らないのですが
2つめ以降の引数が邪魔
;["1","2","3"].map(e => parseInt(e))
// [1, 2, 3]
// [1, 2, 3]
これを 略して
;[1,2,3].map(parseInt)
// [1, NaN, NaN]
// [1, NaN, NaN]
と書くとちゃんと動きません
2 つめの引数が基数なのでインデックスの値のせいで 2 進数や 3 進数として解釈してしまっています
他にも String.fromCharCode などがあります
ES6 のアロー関数は短くかけるのでそこまで負担はないのですが こういうのは関数そのまま渡したいです
どうにかしたいのですが map と同じ動きでコールバックの呼び出しだけ違う関数を用意するのが一番に思えます
でも 全部の関数分作るのって大変だし map や forEach はそのままでどうにかしたいな と思って一応工夫してやってみました
parseInt 的な関数はわかりやすくするため この console.log する関数にします
function fn(a, b){ console.log(a, b) }
pass メソッドを作る
一つ目は 関数にメソッドをつけて まずメソッドの引数に渡した関数を通してから関数自身を実行するようにするものまず普通に forEach すると
;[1,2,3].forEach(fn)
// 1 0
// 2 1
// 3 2
// 1 0
// 2 1
// 3 2
pass メソッドを作ります
Function.prototype.pass = function(f){
return (...args) => this(...f(...args))
}
return (...args) => this(...f(...args))
}
これをつかって 引数をひとつだけにします
const one = a => [a]
;[1,2,3].forEach(fn.pass(one))
// 1 undefined
// 2 undefined
// 3 undefined
;[1,2,3].forEach(fn.pass(one))
// 1 undefined
// 2 undefined
// 3 undefined
one のところは本来
const one_ = (...args) => [args[0]]
のように引数を変換して配列にするものです
最初の引数だけ必要なので省略した書き方にしてます
引数全部そのまま通すなら
const through = (...args) => args
こうです
悪いところは pass も one も作らないとダメなので準備がめんどうです
さらに 毎回書く部分も ES6 かけるなら e => fn(e) のほうが短いです
一応 one のところに他のものをいれることもできますが あまり使いみちもないです
const square = (...args) => args.map(e => e * e)
;[1,2,3].forEach(fn.pass(square))
// 1 0
// 4 1
// 9 4
;[1,2,3].forEach(fn.pass(square))
// 1 0
// 4 1
// 9 4
singlize
基本 1 つだけ欲しい で 2 つとか他の使い方はほぼないと思うので 特化させてしまいます今回は プロトタイプはそのままで別に関数を作ってます
const singlize = f => a => f(a)
;[1,2,3].forEach(singlize(fn))
// 1 undefined
// 2 undefined
// 3 undefined
;[1,2,3].forEach(singlize(fn))
// 1 undefined
// 2 undefined
// 3 undefined
関数名を短くすれば アロー関数よりはタイプしやすいです
関数合成すれば
こういう関数合成する関数を用意してれば余計なことしなくても済みそうに思えましたconst pass = (...fns) => x => fns.reduce((a, b) => b(a), x)
これの使い方は
const calc1 = pass(e => e + 10, e => e * 2, e => e - 1)
calc1(5)
// 29
// (5 + 10) * 2 - 1 を計算してる
calc1(5)
// 29
// (5 + 10) * 2 - 1 を計算してる
という感じ
これを forEach に使います
;[1,2,3].forEach(pass(fn))
// 1 undefined
// 2 undefined
// 3 undefined
// 1 undefined
// 2 undefined
// 3 undefined
引数で渡された複数の関数を順番に適用させていく関数を返すものですが 引数ひとつだけなら その関数を実行するだけの関数です
連結する関係で引数は 1 つになっているので 2 つめ以降は無視されて 一つ目だけ 渡されるのでちょうど良くなります
こういう便利関数は作ってることも多いのでよさそうです
ただ reduce みたいな 2 つという例外は対応できないですけど
filter
なんだかんだ 通す引数の数を指定して制限するのが 目的からすると一番かもFunction.prototype.filter = function(n){
return (...args) => this(...args.slice(0, n))
}
;[1,2,3].forEach(fn.filter(1))
return (...args) => this(...args.slice(0, n))
}
;[1,2,3].forEach(fn.filter(1))
基本 1 つしか使わないのは上書きしてしまうとか
var parseInt = window.parseInt.filter(1)
"1,2,3".split(",").forEach(parseInt)
"1,2,3".split(",").forEach(parseInt)