TLDR: ви можете відфільтрувати масив, а потім виконати свою карту, але для цього знадобиться два проходи на масив (фільтр повертає масив на карту). Оскільки цей масив невеликий, це дуже мала вартість продуктивності. Ви також можете зробити просте зменшення. Однак якщо ви хочете заново уявити собі, як це можна зробити за допомогою одного проходу через масив (або будь-якого типу даних), ви можете використовувати ідею під назвою "перетворювачі", популярну Річ Хікі.
Відповідь:
Нам не потрібно вимагати збільшення ланцюга точок і роботи з масивом, [].map(fn1).filter(f2)...оскільки такий підхід створює проміжні масиви в пам'яті на кожномуreducing функції.
Найкращий підхід працює на фактичній функції зменшення, тому є лише один прохід даних і немає зайвих масивів.
Редукційна функція - це функція, передана в reduceакумулятор і вхід з джерела і повертає щось, схоже на акумулятор
// 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!!!']
Ресурси: розміщуються багаті перетворювачі хіків