.map()上的javascript跳过元素
如何跳过.map中的数组元素?
我的代码:
//map all sources of the images var sources = json.images.map(function (img) { if(img.src.split('.').pop() === "json"){//if extension is .json game.loadSprite(img.src,false,function(){ console.log("sprite loaded!"); }); return null; // skip }else{ return img.src; } });
这将返回
["img.png", null, "img.png"]
刚刚.filter()
它:
var sources = json.images.filter(function(img) { if (img.src.split('.').pop() === "json") { game.loadSprite(img.src,false,function(){ console.log("sprite loaded!"); }); return false; // skip } return true; }).map(function(img) { return img.src; });
如果你不想这样做,这是不合理的,因为它有一定的代价,你可以使用更一般的.reduce()
。 您通常可以用.map()
来表示.map()
:
someArray.map(function(element) { return transform(element); });
可以写成
someArray.reduce(function(result, element) { result.push(transform(element)); return result; }, []);
所以,如果您需要跳过元素,可以使用.reduce()
轻松完成:
var sources = json.images.reduce(function(result, img) { if (img.src.split('.').pop() === "json") { game.loadSprite(img.src, false, function() { console.log("Sprite loaded!"); }); } else { result.push(img.src); } return result; }, []);
在该版本中,第一个示例的.filter()
的代码是.filter()
.reduce()
callback的一部分。 在filter操作保持它的情况下,图像源仅被推送到结果数组上。
更新:所有我以为我知道这个答案是错误的
我们不应该要求增加点链接并在数组[].map(fn1).filter(f2)...
因为这种方法在每个reducing
函数的内存中创build中间数组。
最好的方法是在实际的减less函数上运行,所以只有一个数据传递,没有额外的数组。
reduce
函数是传递到reduce
的函数,并从源中获取累加器和input,并返回类似于累加器的内容
// 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!!!']
这是一个有趣的解决scheme:
/** * Filter-map. Like map, but skips undefined values. * * @param callback */ function fmap(callback) { return this.reduce((accum, ...args) => { let x = callback(...args); if(x !== undefined) { accum.push(x); } return accum; }, []); }
与绑定运算符一起使用 :
[1,2,-1,3]::fmap(x => x > 0 ? x * 2 : undefined); // [2,4,6]
下面是一个只映射非null值的实用程序方法(隐藏调用以减less):
function mapNonNull(arr, cb) { return arr.reduce(function (accumulator, value, index, arr) { var result = cb.call(null, value, index, arr); if (result != null) { accumulator.push(result); } return accumulator; }, []); } var result = mapNonNull(["a", "b", "c"], function (value) { return value === "b" ? null : value; // exclude "b" }); console.log(result); // ["a", "c"]
回答sans多余的边缘情况:
const thingsWithoutNulls = things.reduce((acc, thing) => { if (thing !== null) { return acc.push(thing) } }, [])