如何跳过.map()中的元素?

Ism*_*ail 336 javascript

如何跳过数组元素.map

我的代码:

var sources = images.map(function (img) {
    if(img.src.split('.').pop() === "json"){ // if extension is .json
        return null; // skip
    }
    else{
        return img.src;
    }
});
Run Code Online (Sandbox Code Playgroud)

这将返回:

["img.png", null, "img.png"]
Run Code Online (Sandbox Code Playgroud)

Poi*_*nty 516

就是.filter()第一个:

var sources = images.filter(function(img) {
  if (img.src.split('.').pop() === "json") {
    return false; // skip
  }
  return true;
}).map(function(img) { return img.src; });
Run Code Online (Sandbox Code Playgroud)

如果你不想这样做,这是不合理的,因为它有一些成本,你可以使用更一般.reduce().你一般可以表现.map()在以下方面.reduce:

someArray.map(function(element) {
  return transform(element);
});
Run Code Online (Sandbox Code Playgroud)

可写成

someArray.reduce(function(result, element) {
  result.push(transform(element));
  return result;
}, []);
Run Code Online (Sandbox Code Playgroud)

因此,如果您需要跳过元素,可以通过以下方式轻松完成.reduce():

var sources = images.reduce(function(result, img) {
  if (img.src.split('.').pop() !== "json") {
    result.push(img.src);
  }
  return result;
}, []);
Run Code Online (Sandbox Code Playgroud)

在该版本中,.filter()第一个示例中的代码是.reduce()回调的一部分.在过滤器操作保留它的情况下,仅将图像源推送到结果数组上.

  • 这不是要求你在整个数组上循环两次吗?有什么办法可以避免吗? (15认同)
  • 有了所有这些负面的,"空"式的值(`null`,`undefined`,`NaN`等),如果我们可以利用`map()`中的一个作为这个对象映射到任何东西的指示器,那将是一件好事.应该跳过.我经常遇到数组我要映射98%(例如:`String.split()`在末尾留下一个空的字符串,我不在乎).感谢您的回答 :) (8认同)
  • @AlexMcMillan以及`.reduce()`是基线的"做任何你想要的"功能,因为你可以完全控制返回值.您可能对Rich Hickey在Clojure中关于[传感器]概念(https://www.youtube.com/watch?v=6mTbuzafcII)的出色工作感兴趣. (6认同)
  • @AlexMcMillan你可以使用`.reduce()`并在一次通过中完成所有操作,虽然性能方面我怀疑它会产生重大影响. (5认同)
  • @vsync你不能跳过`.map()`的元素.但是你可以使用`.reduce()`,所以我会添加它. (2认同)
  • @Pointy`if(谓词)返回false,否则返回true`-认真吗?;-) (2认同)
  • 请参阅下一个答案并使用“.flatMap()”代替。 (2认同)

the*_*trk 19

更新:我认为我对此答案所知的一切都是错误的

我们不应该要求增加点链和操作数组,[].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!!!']
Run Code Online (Sandbox Code Playgroud)


mpe*_*pen 15

这是一个有趣的解决方案:

/**
 * 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;
    }, []);
}
Run Code Online (Sandbox Code Playgroud)

bind运算符一起使用:

[1,2,-1,3]::fmap(x => x > 0 ? x * 2 : undefined); // [2,4,6]
Run Code Online (Sandbox Code Playgroud)


sim*_*eco 14

TL; TR;

我认为从数组跳过一些元素的最简单方法是使用filter()方法.

通过使用此方法和ES6语法,您可以在一行中编写代码:

let sources = images.filter(img => img.src.slice(-4) != 'json').map(img => img.src);
Run Code Online (Sandbox Code Playgroud)

这将返回你想要的东西:

let images = [{src: 'img.png'}, {src: 'j1.json'}, {src: 'img.png'}, {src: 'j2.json'}];

let sources = images.filter(img => img.src.slice(-4) != 'json').map(img => img.src);

console.log(sources);
Run Code Online (Sandbox Code Playgroud)

  • 2018年的最佳答案. (3认同)
  • 这是否比“forEach”更好并且一次性完成它而不是两次? (2认同)

cor*_*ons 11

没有多余的边缘案例:

const thingsWithoutNulls = things.reduce((acc, thing) => {
  if (thing !== null) {
    acc.push(thing);
  }
  return acc;
}, [])
Run Code Online (Sandbox Code Playgroud)


Luc*_* P. 11

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);
Run Code Online (Sandbox Code Playgroud)

.filter(Boolean)将在给定阵列中,而你的情况是中滤除任何falsey值null


Ale*_*lex 9

为什么不只使用forEach循环?

let arr = ['a', 'b', 'c', 'd', 'e'];
let filtered = [];

arr.forEach(x => {
  if (!x.includes('b')) filtered.push(x);
});

console.log(filtered)   // filtered === ['a','c','d','e'];
Run Code Online (Sandbox Code Playgroud)

或更简单的使用过滤器:

const arr = ['a', 'b', 'c', 'd', 'e'];
const filtered = arr.filter(x => !x.includes('b')); // ['a','c','d','e'];
Run Code Online (Sandbox Code Playgroud)

  • 我很感激!开心开发有趣的东西! (2认同)

cam*_*ice 7

要推断Felix Kling 的评论,您可以这样使用.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);
Run Code Online (Sandbox Code Playgroud)

这将从返回的数组中删除 falsey 值 .map()

您可以像这样进一步简化它:

var sources = images.map(function (img) {
  if(img.src.split('.').pop() !== "json") { // if extension is .json
    return img.src;
  }
}).filter(Boolean);
Run Code Online (Sandbox Code Playgroud)

或者甚至作为使用箭头函数、对象解构和&&运算符的单行:

var sources = images.map(({ src }) => src.split('.').pop() !== "json" && src).filter(Boolean);
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,```.filter(Boolean)``` 是一个天才的解决方案! (3认同)

Tre*_*xon 5

Array.prototype.flatMap是另一个选项。

images.flatMap(({src}) => src.endsWith('.json') && [] || src);
Run Code Online (Sandbox Code Playgroud)

从MDN

flatMap可以用作在地图中添加和删除项目(修改项目数)的方法。换句话说,它允许您将许多项目映射到许多项目(通过分别处理每个输入项目),而不是始终一对一。从这个意义上讲,它的作用类似于过滤器。只需返回一个1元素数组以保留该项目,返回一个多元素数组以添加项目,或返回0元素数组以删除该项目。

  • 最佳答案放下!更多信息请参见:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap#For_adding_and_removing_items_during_a_map (7认同)
  • 首先,感谢 MDN 提供此类评论。文档中包含此类实际用例示例的情况并不常见。其次,我确实希望它对_稍微更有效_部分更具体。比“map”后跟“flat”效率高多少? (5认同)
  • 精彩的功能!带有 nullish 合并的更短的现代语法: foo.flatMap(bar => bar.baz ?? [ ]) (5认同)
  • 这才是真正的答案,简单而有力。我们了解到这比过滤和减少更好。 (4认同)
  • 这应该是公认的答案!效果就像魅力! (3认同)
  • 你为我发现了多么可爱的功能啊。谢谢你! (3认同)
  • 太棒了,但我希望有一个不需要返回空数组的本机方法 - 也许是一个将所有真值放入新数组并跳过假值的方法。 (2认同)
  • 我投票支持这个解决方案。关于性能,它的一轮比 2 轮过滤+映射操作更快是合乎逻辑的,除非它的“扁平化”部分导致性能下降 (2认同)