Home > OS >  Got undefined in map result
Got undefined in map result

Time:03-26

I'm having trouble converting, summing, and sorting the following arrays into key and value objects

Data Array

0: {No: '1', Product Name: 'Harry Potter', Type: 'Novel', Price: '120', Url: 'https://harry-potter'}
1: {No: '2', Product Name: 'Harry Potter', Type: 'Novel', Price: '100', Url: 'https://harry-potter'}
2: {No: '3', Product Name: 'Naruto', Type: 'Comic', Price: '68', Url: 'https://naruto'}
n: {No: '...', Product Name: '...', Type: '...', Price: '...', Url: '...'}

my current code

let counts = myRows.reduce((prev, curr) => {
  let count = prev.get(curr["Product Name"]) || 0;
  prev.set(
    curr["Product Name"],
    parseFloat(curr["Product Name"])   count,
    curr["Url"]
  );
  return prev;
}, new Map());

// then, map your counts object back to an array
let reducedObjArr = [...counts].map(([key, value, link]) => {
  return { key, value, link };
});

// SORT BY DESCENDING VALUE
var desc = reducedObjArr.sort((a, b) => b.value - a.value);

console.log(desc);

and the result of my current code

0:
key: "Harry Potter"
link: undefined
value: 220
1:
key: "Naruto"
link: undefined
value: 68

though, the result I want is like this

0:
key: "Harry Potter"
link: "https://harry-potter"
value: 220
1:
key: "Naruto"
link: "https://naruto"
value: 68

CodePudding user response:

Map.prototype.set() only takes 2 arguments, you're passing 3. If you want to store multiple values in a map key, store them as an array or object. In my code below, I store [price, url].

Another problem is that you were trying to parse curr["Product Name"] as the price, but it should be curr.Price.

const myRows = [
  {No: '1', "Product Name": 'Harry Potter', Type: 'Novel', Price: '120', Url: 'https://harry-potter'},
  {No: '2', "Product Name": 'Harry Potter', Type: 'Novel', Price: '100', Url: 'https://harry-potter'},
  {No: '3', "Product Name": 'Naruto', Type: 'Comic', Price: '68', Url: 'https://naruto'}
];

let counts = myRows.reduce((prev, curr) => {
  let count = prev.get(curr["Product Name"])?.[0] || 0;
  prev.set(
    curr["Product Name"], 
    [parseFloat(curr.Price)   count,
      curr.Url
    ]
  );
  return prev;
}, new Map());

// then, map your counts object back to an array
let reducedObjArr = [...counts].map(([key, [value, link]]) => {
  return {
    key,
    value,
    link
  };
});

// SORT BY DESCENDING VALUE
var desc = reducedObjArr.sort((a, b) => b.value - a.value);

console.log(desc);

CodePudding user response:

As an alternative to Map you could also build a JS object using reduce() and in the end use Object.values() to get the values as an array.

const data = [
  {
    No: "1",
    "Product Name": "Harry Potter",
    Type: "Novel",
    Price: "120",
    Url: "https://harry-potter",
  },
  {
    No: "2",
    "Product Name": "Harry Potter",
    Type: "Novel",
    Price: "100",
    Url: "https://harry-potter",
  },
  {
    No: "3",
    "Product Name": "Naruto",
    Type: "Comic",
    Price: "68",
    Url: "https://naruto",
  },
];


const result = data.reduce((prev, movie) => {
  // destruct properties for convenience to have shorter name
  const { "Product Name": key, Url: link, Price: value } = movie;
  // parse the value
  const floatValue = parseFloat(value) || 0;
  // use previous value to compute new accumulated value if there is a previous value
  const accValue = prev[key] ? prev[key].value   floatValue: floatValue;
  // set and return new value
  prev[key] = {key: key, link: link, value: accValue};
  return prev;
}, {})
console.log(Object.values(result))

CodePudding user response:

I would solve it with an Array.reduce like this:

  1. I would first initialize the value that reduce is going to return to an empty array.

  2. It would then check if the item already exists in the accumulator.

    1. If it doesn't exist, I simply return a new array, with the previous elements in the array and the current iteration value.

    2. If it exists, I iterate the accumulator with map to return a new array, go back and check that the object exists, if it exists I add the current amount of the map iteration to the current amount of the reduce iteration, if it doesn't exist I just return the current value of the iteration within the map.

let myRows = [
    { No: 1, "Product Name": 'Harry Potter', Type: 'Novel', Price: '120', Url: 'https://harry-potter' }, 
    { No: 2, "Product Name": 'Harry Potter', Type: 'Novel', Price: '100', Url: 'https://harry-potter'},
    { No: 3, "Product Name": 'Naruto', Type: 'Comic', Price: '68', Url: 'https://naruto' }
];

const counts = myRows.reduce((accumulator, currentValue) => {
  const elementAlreadyExists = accumulator.find(element => element["Product Name"] === currentValue["Product Name"]);
  if (elementAlreadyExists) {
    return accumulator.map((element) => {
      if (element["Product Name"] === currentValue["Product Name"]) {
        return {
          ...element,
          Price: parseFloat(element.Price)   parseFloat(currentValue.Price)
        }
      }
      return element;
    });
  }

  return [...accumulator, currentValue];
}, []);

console.log(counts);

CodePudding user response:

The below solution may achieve the same desired objective:

Code Snippet

const groupSumSort = arr => (
  Object.values(
    arr.reduce(
      (fin, itm) => ({
        ...fin,
        [itm["Product Name"]]: (
          fin[itm["Product Name"]]
          ? {
            ...fin[itm["Product Name"]],
            value: fin[itm["Product Name"]].value    itm.Price
          }
          : { key: itm["Product Name"], link: itm.Url, value:  itm.Price }
        )
      }),
      {}
    )
  ).sort(
    (a, b) => b.value - a.value
  )
);


const myRows = [
  {No: '1', "Product Name": 'Harry Potter', Type: 'Novel', Price: '120', Url: 'https://harry-potter'},
  {No: '2', "Product Name": 'Harry Potter', Type: 'Novel', Price: '100', Url: 'https://harry-potter'},
  {No: '3', "Product Name": 'Naruto', Type: 'Comic', Price: '68', Url: 'https://naruto'}
];

console.log(groupSumSort(myRows));

Explanation

This solution differs from OP's solution in below aspects.

  • It considers the accumulator named fin to be an empty object {} initially instead of new Map()
  • Instead of .parseFloat, it uses itm.Price to convert string to number
  • Since fin is already an object, no need to create a variable: reducedObjArr
  • The values from .reduce contains key, link, value props
  • These values are extracted using Object.values
  • The sorting is done on the values extracted
  • The entire logic described above is woven into a method groupSumSort to help with readability of the rest of the code
  • Related