How to skip over an element in .map()?

前端 未结 16 1931
余生分开走
余生分开走 2020-11-27 09:26

How can I skip an array element in .map?

My code:

var sources = images.map(function (img) {
    if(img.src.split(\'.\').pop() === \"json         


        
相关标签:
16条回答
  • 2020-11-27 09:52

    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:

    [1,2,-1,3]::fmap(x => x > 0 ? x * 2 : undefined); // [2,4,6]
    
    0 讨论(0)
  • 2020-11-27 09:52

    You can do this

    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
        }
    });

    0 讨论(0)
  • 2020-11-27 09:54

    To extrapolate on Felix Kling's comment, you can use .filter() like this:

    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);
    

    That will remove falsey values from the array that is returned by .map()

    You could simplify it further like this:

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

    Or even as a one-liner using an arrow function, object destructuring and the && operator:

    var sources = images.map(({ src }) => src.split('.').pop() !== "json" && src).filter(Boolean);
    
    0 讨论(0)
  • 2020-11-27 09:57

    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). Since this array is small, it is a very small performance cost. You can also do a simple reduce. However if you want to re-imagine how this can be done with a single pass over the array (or any datatype), you can use an idea called "transducers" made popular by 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.

    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

    // 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

    0 讨论(0)
  • 2020-11-27 09:57

    if it null or undefined in one line ES5/ES6

    //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);
    
    0 讨论(0)
  • 2020-11-27 09:58

    Why not just use a forEach loop?

    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'];

    Or even simpler use filter:

    const arr = ['a', 'b', 'c', 'd', 'e'];
    const filtered = arr.filter(x => !x.includes('b')); // ['a','c','d','e'];
    
    0 讨论(0)
提交回复
热议问题