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

本文翻译自:How to skip over an element in .map()?

How can I skip an array element in .map ? 如何跳过.map的数组元素?

My code: 我的代码:

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

This will return: 这将返回:

["img.png", null, "img.png"]

#1楼

参考:https://stackoom.com/question/1g5NE/如何跳过-map-中的元素


#2楼

Just .filter() it first: 只是.filter()首先:

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

If you don't want to do that, which is not unreasonable since it has some cost, you can use the more general .reduce() . 如果您不想这样做,因为这样做要花一些钱,这并不是不合理的,则可以使用更通用的.reduce() You can generally express .map() in terms of .reduce : 你通常可以表达.map()中的条款.reduce

someArray.map(function(element) {
  return transform(element);
});

can be written as 可以写成

someArray.reduce(function(result, element) {
  result.push(transform(element));
  return result;
}, []);

So if you need to skip elements, you can do that easily with .reduce() : 因此,如果您需要跳过元素,则可以使用.reduce()轻松实现:

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

In that version, the code in the .filter() from the first sample is part of the .reduce() callback. 在该版本中,第一个示例的.filter()的代码是.filter() .reduce()回调的一部分。 The image source is only pushed onto the result array in the case where the filter operation would have kept it. 仅在过滤操作可以保留结果的情况下,才将图像源推入结果数组。


#3楼

TLDR: You can first filter your array and then perform your map but this would require two passes on the array (filter returns an array to map). TLDR:您可以先过滤数组,然后执行地图,但这需要对数组进行两次遍历 (过滤器将数组返回到地图)。 Since this array is small, it is a very small performance cost. 由于此阵列很小,因此性能成本非常低。 However if you want to imagine how this can be done with a single pass over the array, you can use an idea called "transducers" made popular by Rich Hickey. 但是,如果您想想象一下如何通过一次遍历数组就可以完成此操作,则可以使用Rich Hickey流行的一种名为“换能器”的想法。

Answer: 回答:

We should not require increasing dot chaining and operating on the array [].map(fn1).filter(f2)... since this approach creates intermediate arrays in memory on every reducing function. 我们不应该需要增加点链接和所述阵列上操作的[].map(fn1).filter(f2)...因为这种方法在存储器上的每个创建中间阵列reducing功能。

The best approach operates on the actual reducing function so there is only one pass of data and no extra arrays. 最好的方法是对实际的约简函数进行操作,因此只有一轮数据,没有额外的数组。

The reducing function is the function passed into reduce and takes an accumulator and input from the source and returns something that looks like the accumulator 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!!!']

Resources: rich hickey transducers post 资源: Rich Hickey传感器发布


#4楼

Here's a fun solution: 这是一个有趣的解决方案:

/**
 * 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;
    }, []);
}

Use with the bind operator : bind运算符一起使用

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

#5楼

Here's a utility method (ES5 compatible) which only maps non null values (hides the call to reduce): 这是一个实用程序方法(与ES5兼容),它仅映射非null值(隐藏了reduce的调用):

 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"] 


#6楼

Answer sans superfluous edge cases: 回答无用的边缘情况:

const thingsWithoutNulls = things.reduce((acc, thing) => {
  if (thing !== null) {
    acc.push(thing);
  }
  return acc;
}, [])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值