如何跳过.map
中的数组元素?
我的代码:
var sources = images.map(function (img) {
if(img.src.split('.').pop() === "json"){ // if extension is .json
return null; // skip
}
else{
return img.src;
}
});
这将返回:
["img.png", null, "img.png"]
答案 0 :(得分:429)
首先.filter()
:
var sources = images.filter(function(img) {
if (img.src.split('.').pop() === "json") {
return false; // skip
}
return true;
}).map(function(img) { return img.src; });
如果您不想这样做,这不是不合理的,因为它有一些成本,您可以使用更一般的.reduce()
。您通常可以.map()
:
.reduce
someArray.map(function(element) {
return transform(element);
});
可以写成
someArray.reduce(function(result, element) {
result.push(transform(element));
return result;
}, []);
因此,如果您需要跳过元素,可以使用.reduce()
轻松完成:
var sources = images.reduce(function(result, img) {
if (img.src.split('.').pop() !== "json") {
result.push(img.src);
}
return result;
}, []);
在该版本中,第一个示例中的.filter()
中的代码是.reduce()
回调的一部分。在过滤操作保留的情况下,图像源仅被推送到结果数组上。
答案 1 :(得分:17)
TLDR:您可以先过滤数组,然后执行地图,但这需要在数组上进行两次传递(过滤器返回一个要映射的数组)。由于该阵列很小,因此性能成本非常低。但是,如果您想要想象如何通过对阵列进行单次传递来完成此操作,则可以使用名为"传感器"受到Rich Hickey的欢迎。
答案:
我们不应该要求增加点链并对数组[].map(fn1).filter(f2)...
进行操作,因为这种方法会在每个reducing
函数的内存中创建中间数组。
最好的方法是对实际的减少函数进行操作,因此只有一次数据传递而没有额外的数组。
reduce函数是传递给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!!!']
答案 2 :(得分:13)
这是一个有趣的解决方案:
/**
* 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]
答案 3 :(得分:10)
回答没有多余的边缘情况:
android.app.Application
答案 4 :(得分:10)
我认为从数组中跳过某些元素的最简单方法是使用filter()方法。
使用此方法和 ES6 语法,您可以用一行编写代码:
var sources = images.filter(img => img.src.slice(-4) != "json").map(img => img.src);
,这将返回您想要的内容:
["img.png", "img.png"]
答案 5 :(得分:6)
为什么不只使用forEach循环?
let arr = ['a','b','c','d','e'];
let filtered = [];
arr.forEach(x => {
if (!x.includes('b')) filtered.push(x);
});
// filtered === ['a','c','d','e'];
答案 6 :(得分:3)
这是一个实用程序方法(ES5兼容),它只映射非空值(隐藏调用以减少):
MathFullForm
答案 7 :(得分:2)
要在Felix Kling's comment上进行推断,可以像这样使用.filter()
:
var sources = images.map(function (img) {
if(img.src.split('.').pop() === "json") { // if extension is .json
return null; // skip
} else {
return img.src;
}
}).filter(Boolean);
这将从.map()
返回的数组中删除虚假值
您可以像这样进一步简化它:
var sources = images.map(function (img) {
if(img.src.split('.').pop() !== "json") { // if extension is .json
return img.src;
}
}).filter(Boolean);
或者甚至是使用箭头功能,对象解构和&&
运算符的单行代码:
var sources = images.map(({ src }) => src.split('.').pop() !== "json" && src).filter(Boolean);
答案 8 :(得分:1)
Array.prototype.flatMap是另一种选择。
images.flatMap(({src}) => src.endsWith('.json') && [] || src);
flatMap
可用作添加和删除项目的方法(修改 地图中的项数)。换句话说,它允许您映射 许多项目到许多项目(通过分别处理每个输入项目), 而不是总是一对一的。从这个意义上讲,它就像 过滤器的对面。只需返回一个1元素数组即可保留该项目, 一个多元素数组以添加项目,或一个0元素数组以删除项目 该项目。
答案 9 :(得分:1)
我使用.forEach
进行迭代,然后将结果推入results
数组,然后使用它,通过这种解决方案,我不会在数组上循环两次
答案 10 :(得分:1)
您可以这样做
var sources = [];
images.map(function (img) {
if(img.src.split('.').pop() !== "json"){ // if extension is not .json
sources.push(img.src); // just push valid value
}
});
答案 11 :(得分:0)
var sources = images.map(function (img) {
if(img.src.split('.').pop() === "json"){ // if extension is .json
return null; // skip
}
else{
return img.src;
}
}).filter(Boolean);
.filter(Boolean)
将过滤出给定数组(在您的情况下为null
)中的所有虚假值。
答案 12 :(得分:0)
这是code provided by @theprtk的更新版本。进行了一些整理以显示通用版本,同时提供示例。
注意:我会将其添加为他的帖子的评论,但我的声誉还不够
/**
* @see http://clojure.com/blog/2012/05/15/anatomy-of-reducer.html
* @description functions that transform reducing functions
*/
const transduce = {
/** a generic map() that can take a reducing() & return another reducing() */
map: changeInput => reducing => (acc, input) =>
reducing(acc, changeInput(input)),
/** a generic filter() that can take a reducing() & return */
filter: predicate => reducing => (acc, input) =>
predicate(input) ? reducing(acc, input) : acc,
/**
* a composing() that can take an infinite # transducers to operate on
* reducing functions to compose a computed accumulator without ever creating
* that intermediate array
*/
compose: (...args) => x => {
const fns = args;
var i = fns.length;
while (i--) x = fns[i].call(this, x);
return x;
},
};
const example = {
data: [{ src: 'file.html' }, { src: 'file.txt' }, { src: 'file.json' }],
/** note: `[1,2,3].reduce(concat, [])` -> `[1,2,3]` */
concat: (acc, input) => acc.concat([input]),
getSrc: x => x.src,
filterJson: x => x.src.split('.').pop() !== 'json',
};
/** step 1: create a reducing() that can be passed into `reduce` */
const reduceFn = example.concat;
/** step 2: transforming your reducing function by mapping */
const mapFn = transduce.map(example.getSrc);
/** step 3: create your filter() that operates on an input */
const filterFn = transduce.filter(example.filterJson);
/** step 4: aggregate your transformations */
const composeFn = transduce.compose(
filterFn,
mapFn,
transduce.map(x => x.toUpperCase() + '!'), // new mapping()
);
/**
* Expected example output
* Note: each is wrapped in `example.data.reduce(x, [])`
* 1: ['file.html', 'file.txt', 'file.json']
* 2: ['file.html', 'file.txt']
* 3: ['FILE.HTML!', 'FILE.TXT!']
*/
const exampleFns = {
transducers: [
mapFn(reduceFn),
filterFn(mapFn(reduceFn)),
composeFn(reduceFn),
],
raw: [
(acc, x) => acc.concat([x.src]),
(acc, x) => acc.concat(x.src.split('.').pop() !== 'json' ? [x.src] : []),
(acc, x) => acc.concat(x.src.split('.').pop() !== 'json' ? [x.src.toUpperCase() + '!'] : []),
],
};
const execExample = (currentValue, index) =>
console.log('Example ' + index, example.data.reduce(currentValue, []));
exampleFns.raw.forEach(execExample);
exampleFns.transducers.forEach(execExample);
答案 13 :(得分:0)
您可以使用之后的方法map()
。方法filter()
例如:
var sources = images.map(function (img) {
if(img.src.split('.').pop() === "json"){ // if extension is .json
return null; // skip
}
else {
return img.src;
}
});
方法过滤器:
const sourceFiltered = sources.filter(item => item)
然后,只有现有项目在新数组sourceFiltered
中。
答案 14 :(得分:0)
如果在一行ES5 / ES6中为null或未定义
//will return array of src
images.filter(p=>!p.src).map(p=>p.src);//p = property
//in your condition
images.filter(p=>p.src.split('.').pop() !== "json").map(p=>p.src);