I have an array of objects like this
const inputArray = [
{ name: "sam", date: "1 / 1 / 23", confirmed: "yes", spent: 0 },
{ name: "sam", date: "1 / 2 / 23", confirmed: "yes", spent: 4 },
{ name: "sam", date: "1 / 3 / 23", confirmed: "yes", spent: 4 },
{ name: "sam", date: "1 / 4 / 23", confirmed: "no", spent: 4 },
{ name: "bill", date: "1 / 5 / 23", confirmed: "yes", spent: 4 },
{ name: "bill", date: "1 / 6 / 23", confirmed: "yes", spent: 4 },
{ name: "bill", date: "1 / 5 / 23", confirmed: "yes", spent: 0 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "yes", spent: 0 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "no", spent: 2 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "no", spent: 2 },
];
I want an output object like this
- value of key
spent
is just an addition of allspent
for aname
- value of key
confirmedAndNotSpent
is just the addition of allconfirmed
as 'yes' butspent
as 0 for aname
- value of key
notConfirmedAndSpent
is just an addition of allconfirmed
as 'no' for aname
const outputObj = {
sam: { spent: 12, confirmedAndNotSpent: 1, notConfirmedAndSpent: 1 },
bill: { spent: 8, confirmedAndNotSpent: 1, notConfirmedAndSpent: 0 },
bill: { spent: 4, confirmedAndNotSpent: 1, notConfirmedAndSpent: 2 },
};
How do I achieve this.
I tired this first
let try1 = inputArray.reduce((accumulator, current) => {
if (!accumulator[current.name]) accumulator[current.name] = 0;
accumulator[current.name] = current.spent;
return accumulator;
}, {});
This gives the the output like so { sam: 12, bill: 8, annie: 4 }
.
However, I am unable to convert even this into an object with the code below.
let try2 = inputArray.reduce((accumulator, current) => {
if (!accumulator[current.name]) accumulator[current.name] = {};
accumulator[current.name][current.spent] = current.spent;
return accumulator;
}, {})
The above code gives the out put below
{
sam: { '0': NaN, '4': NaN },
bill: { '0': NaN, '4': NaN },
annie: { '0': NaN, '2': NaN }
}
Can anyone help to get the output I want? Thanks.
CodePudding user response:
Two issues with your code:
- You made a typo. Where you declare
try2
, this line:
accumulator[current.name][current.spent] = current.spent;
should be:
accumulator[current.name].spent = current.spent;
- In JavaScript,
undefined 0
(or any number) is equal to NaN. To solve this. You have to first initialize yourspent
property to 0:
if (!accumulator[current.name]) accumulator[current.name] = { spent: 0 }
For the other variables, these can all be accomplished in a similar manner, just with the necessary conditional checks.
const inputArray = [
{ name: "sam", date: "1 / 1 / 23", confirmed: "yes", spent: 0 },
{ name: "sam", date: "1 / 2 / 23", confirmed: "yes", spent: 4 },
{ name: "sam", date: "1 / 3 / 23", confirmed: "yes", spent: 4 },
{ name: "sam", date: "1 / 4 / 23", confirmed: "no", spent: 4 },
{ name: "bill", date: "1 / 5 / 23", confirmed: "yes", spent: 4 },
{ name: "bill", date: "1 / 6 / 23", confirmed: "yes", spent: 4 },
{ name: "bill", date: "1 / 5 / 23", confirmed: "yes", spent: 0 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "yes", spent: 0 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "no", spent: 2 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "no", spent: 2 },
];
let try2 = inputArray.reduce((accumulator, current) => {
if (!accumulator[current.name]) accumulator[current.name] = {
spent: 0,
confirmedAndNotSpent: 0,
notConfirmedAndSpent: 0
};
accumulator[current.name].spent = current.spent;
if(current.confirmed === "yes" && current.spent === 0) {
accumulator[current.name].confirmedAndNotSpent ;
}
if(current.confirmed === "no") {
accumulator[current.name].notConfirmedAndSpent ;
}
return accumulator;
}, {})
document.getElementById("out").innerText = JSON.stringify(try2, null, 4)
<pre id="out"></p>
CodePudding user response:
When iterating over an array item, perform all the checks you need in that one iteration - increment spent
, increment confirmedAndNotSpent
if appropriate, and increment notConfirmedAndSpent
if appropriate. Make the accumulator (or outputObj
) an object of objects - the value should be an object, not just a number, to hold all the information you want.
const inputArray = [
{ name: "sam", date: "1 / 1 / 23", confirmed: "yes", spent: 0 },
{ name: "sam", date: "1 / 2 / 23", confirmed: "yes", spent: 4 },
{ name: "sam", date: "1 / 3 / 23", confirmed: "yes", spent: 4 },
{ name: "sam", date: "1 / 4 / 23", confirmed: "no", spent: 4 },
{ name: "bill", date: "1 / 5 / 23", confirmed: "yes", spent: 4 },
{ name: "bill", date: "1 / 6 / 23", confirmed: "yes", spent: 4 },
{ name: "bill", date: "1 / 5 / 23", confirmed: "yes", spent: 0 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "yes", spent: 0 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "no", spent: 2 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "no", spent: 2 },
];
const output = {};
for (const { name, date, confirmed, spent } of inputArray) {
output[name] ??= { spent: 0, confirmedAndNotSpent: 0, notConfirmedAndSpent: 0 };
output[name].spent = spent;
if (confirmed === 'yes' && spent === 0) {
output[name].confirmedAndNotSpent ;
}
if (confirmed === 'no') {
output[name].notConfirmedAndSpent ;
}
}
console.log(output);
CodePudding user response:
const inputArray = [
{ name: "sam", date: "1 / 1 / 23", confirmed: "yes", spent: 0 },
{ name: "sam", date: "1 / 2 / 23", confirmed: "yes", spent: 4 },
{ name: "sam", date: "1 / 3 / 23", confirmed: "yes", spent: 4 },
{ name: "sam", date: "1 / 4 / 23", confirmed: "no", spent: 4 },
{ name: "bill", date: "1 / 5 / 23", confirmed: "yes", spent: 4 },
{ name: "bill", date: "1 / 6 / 23", confirmed: "yes", spent: 4 },
{ name: "bill", date: "1 / 5 / 23", confirmed: "yes", spent: 0 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "yes", spent: 0 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "no", spent: 2 },
{ name: "annie", date: "1 / 6 / 23", confirmed: "no", spent: 2 },
];
const output = inputArray.reduce((acc, {name, confirmed, spent: currSpent}) => {
let {spent, confirmedAndNotSpent, notConfirmedAndSpent} = acc[name] || {spent: 0, confirmedAndNotSpent: 0, notConfirmedAndSpent: 0}
spent = currSpent
confirmedAndNotSpent = confirmed === 'yes' && currSpent === 0
notConfirmedAndSpent = confirmed === 'no'
return {...acc, [name]: {spent, confirmedAndNotSpent, notConfirmedAndSpent, }}
}, {})
console.log(output)
produces:
{
annie: {
confirmedAndNotSpent: 1,
notConfirmedAndSpent: 2,
spent: 4
},
bill: {
confirmedAndNotSpent: 1,
notConfirmedAndSpent: 0,
spent: 8
},
sam: {
confirmedAndNotSpent: 1,
notConfirmedAndSpent: 1,
spent: 12
}
}
The idea is to reduce the input array to the desired object as you started to do, but accumulate entire objects for each name, rather than just individual fields of the goal objects, and to create the per-name-default objects with the desired keys and appropriate initial values.