0

I have an array that is about 1000 in length. Why is map function returning undefined in certain indexes? Is there a way to only return array that meets this condition? I'd like to return an array with values > 0.

 var total_percents = cars.map(function(element) {
        var savings_percent = Number(element[0].getAttribute("percent-savings"));
        if (savings_percent > 0)
            return savings_percent;
});
Michael
  • 403
  • 1
  • 9
  • 28
  • 1
    You only return a value for one side of the conditional. What if `savings_percent <= 0` ? it looks like you might want to use `reduce` rather than `map`. – Randy Casburn May 11 '18 at 00:20
  • 1
    The result of `map` will be an array the same length as the original. If the `map` callback does not return a value, the result is undefined at that index. Sounds like you want to filter first – Phil May 11 '18 at 00:26
  • 1
    If `savings_percent` is <= 0 you don't return a specific value, and `undefined` is returned. You could use `filter` to only return the wanted elements, or `reduce` to process it all at once. – some May 11 '18 at 00:28
  • What do you want the value of `total_percents` to be? A single number, an array of numbers or an array of _car_ values (whatever they are)? – Phil May 11 '18 at 00:33

4 Answers4

2

You need to filter values after mapping, which you can do with filter array method with predicate like car => car > 0

var total_percents = cars.map((element) => Number(element[0].getAttribute("percent-savings"))).filter(car => car > 0)

You could also use reduce method for combining both operations at once:

var total_percents =
  cars.reduce((acc, element) => {
    const percentSavings = Number(element[0].getAttribute("percent-savings"));

    if (percentSavings > 0) {
      acc.push(percentSavings);
    }

    return acc;
  }, [])
Karen Grigoryan
  • 5,234
  • 2
  • 21
  • 35
  • 2
    A slight correction is required on this. OP wants values over `0` but this will return any _truthy_ value, including negative numbers. Try `car => car > 0` – Phil May 11 '18 at 00:28
  • This is inefficient because you're enumerating the list twice, see my answer using Array.prototype.reduce – MrPizzaFace May 11 '18 at 00:37
  • @fabbb the question was not about ultimate performance or performance at all whatsoever. – Karen Grigoryan May 11 '18 at 00:41
  • @fabbb but I was editing my answer anyways to add also `reduce` version, even before you commented :) – Karen Grigoryan May 11 '18 at 00:44
  • Thanks so much Karen!! Great answer too! If you have time would you be able to take a look at this post? I'm still having trouble with this!! Been stuck on this all day lol https://stackoverflow.com/questions/50278228/matching-objects-in-array-and-consolidate – Michael May 11 '18 at 00:52
1

Unsure what you're attempting to accomplish, but:

Try something like this to return a sum of all pctSavings:

const totalPercents = cars.reduce((sum, el) => {
    const pctSavings = +(el[0].getAttribute("percent-savings"));
    if (pctSavings > 0) sum += pctSavings;
    return sum;
}, 0);

To return an array of pctSavings, simply do this:

const totalPercents = cars.reduce((arr, el) => {
    const pctSavings = +(el[0].getAttribute("percent-savings"));
    if (pctSavings > 0) arr.push(pctSavings);
    return arr;
}, []);

To get the max pctSavings do this:

let maxPctSavings = 0;
cars.forEach(el => {
    const pctSavings = +(el[0].getAttribute("percent-savings"));
    if (pctSavings > maxPctSavings) maxPctSavings = pctSavings
});
console.log(maxPctSavings) // this is your answer
MrPizzaFace
  • 7,807
  • 15
  • 79
  • 123
1

Using reduce to both filter and map the value (should be faster and use less memory than separate filter and map)

var total_percents = cars.reduce(
  function(out, element) {
    var savings_percent = Number(element[0].getAttribute("percent-savings"));
    if (savings_percent > 0) {
      // Only add to the out-array if value is greater than 0.
      out.push(savings_percent);
    }
    return out;
  },
  [] // start with empty array
);

total_percents will be an array, with values that is greater than 0.

Reading from a comment on another answer, what you wanted was the max savings. Then I would do it like this:

var max_savings = cars.reduce(
  function(value, element) {
    var savings_percent = Number(element[0].getAttribute("percent-savings"));
    return Math.max(savings_percent, value)
  },
  0 // start with no savings
);
some
  • 48,070
  • 14
  • 77
  • 93
0

"Number" will return "NaN" (Not a Number) if the argument cannot be converted to a number. Then you are using numeric comparison ">" of a string "NaN" to your zero value, so that comparison fails and does not return, therefore the "undefined" occurs. You could do a double comparison - if (typeof savings_percent == 'string'

if (typeof savings_percent == 'number' && savings_percent > 0)
mxrxkx
  • 1
  • You are correct that `Number` will return `NaN` if the argument can't be converted to a number, but it is **NOT** a string. `typeof Number('string')` is `number`. `NaN` is a special value, that isn't equal to anything, not even itself: `NaN == NaN` is `false`. – some May 11 '18 at 00:46
  • And also typeof `NaN` is `number`, it's better to check `NaN` with new shiny `Number.isNaN()` method to avoid the pitfalls of old `isNaN` – Karen Grigoryan May 11 '18 at 00:53
  • Thanks Karen, I forgot about the isNaN() method! – mxrxkx May 11 '18 at 05:11