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:
I would first initialize the value that reduce is going to return to an empty array.
It would then check if the item already exists in the accumulator.
If it doesn't exist, I simply return a new array, with the previous elements in the array and the current iteration value.
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
namedfin
to be an empty object{}
initially instead ofnew Map()
- Instead of
.parseFloat
, it usesitm.Price
to convert string to number - Since
fin
is already an object, no need to create a variable:reducedObjArr
- The values from
.reduce
containskey
,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