TLDR: Önce dizinizi filtreleyip haritanızı gerçekleştirebilirsiniz, ancak bu dizide iki geçiş gerektirir (filtre eşlemek için bir dizi döndürür). Bu dizi küçük olduğundan, çok düşük bir performans maliyetidir. Ayrıca basit bir azaltma yapabilirsiniz. Bununla birlikte, bunun dizi (veya herhangi bir veri türü) üzerinden tek bir geçişle nasıl yapılabileceğini yeniden hayal etmek istiyorsanız, Rich Hickey tarafından popüler hale getirilen "dönüştürücüler" adlı bir fikir kullanabilirsiniz.
Cevap:
[].map(fn1).filter(f2)...
Bu yaklaşım, her reducing
işlevde bellekte ara diziler oluşturduğundan dizi üzerinde nokta zincirinin artırılmasını ve dizide çalışmasını gerektirmemeliyiz.
En iyi yaklaşım, gerçek azaltma işlevi üzerinde çalışır, bu nedenle yalnızca bir veri geçişi vardır ve fazladan diziler yoktur.
İndirgeme fonksiyonu, reduce
aküye ve kaynaktan bir akümülatör ve giriş alır ve akümülatöre benzeyen bir şey döndüren fonksiyondur.
// 1. create a concat reducing function that can be passed into `reduce`
const concat = (acc, input) => acc.concat([input])
// note that [1,2,3].reduce(concat, []) would return [1,2,3]
// transforming your reducing function by mapping
// 2. create a generic mapping function that can take a reducing function and return another reducing function
const mapping = (changeInput) => (reducing) => (acc, input) => reducing(acc, changeInput(input))
// 3. create your map function that operates on an input
const getSrc = (x) => x.src
const mappingSrc = mapping(getSrc)
// 4. now we can use our `mapSrc` function to transform our original function `concat` to get another reducing function
const inputSources = [{src:'one.html'}, {src:'two.txt'}, {src:'three.json'}]
inputSources.reduce(mappingSrc(concat), [])
// -> ['one.html', 'two.txt', 'three.json']
// remember this is really essentially just
// inputSources.reduce((acc, x) => acc.concat([x.src]), [])
// transforming your reducing function by filtering
// 5. create a generic filtering function that can take a reducing function and return another reducing function
const filtering = (predicate) => (reducing) => (acc, input) => (predicate(input) ? reducing(acc, input): acc)
// 6. create your filter function that operate on an input
const filterJsonAndLoad = (img) => {
console.log(img)
if(img.src.split('.').pop() === 'json') {
// game.loadSprite(...);
return false;
} else {
return true;
}
}
const filteringJson = filtering(filterJsonAndLoad)
// 7. notice the type of input and output of these functions
// concat is a reducing function,
// mapSrc transforms and returns a reducing function
// filterJsonAndLoad transforms and returns a reducing function
// these functions that transform reducing functions are "transducers", termed by Rich Hickey
// source: http://clojure.com/blog/2012/05/15/anatomy-of-reducer.html
// we can pass this all into reduce! and without any intermediate arrays
const sources = inputSources.reduce(filteringJson(mappingSrc(concat)), []);
// [ 'one.html', 'two.txt' ]
// ==================================
// 8. BONUS: compose all the functions
// You can decide to create a composing function which takes an infinite number of transducers to
// operate on your reducing function to compose a computed accumulator without ever creating that
// intermediate array
const composeAll = (...args) => (x) => {
const fns = args
var i = fns.length
while (i--) {
x = fns[i].call(this, x);
}
return x
}
const doABunchOfStuff = composeAll(
filtering((x) => x.src.split('.').pop() !== 'json'),
mapping((x) => x.src),
mapping((x) => x.toUpperCase()),
mapping((x) => x + '!!!')
)
const sources2 = inputSources.reduce(doABunchOfStuff(concat), [])
// ['ONE.HTML!!!', 'TWO.TXT!!!']
Kaynaklar: rich hickey transducers post