Update nested Array Object Array in React State - javascript

I'm trying to update qty of product onClick event using useState in react, the data that I am updating is nested array-object-array
Below is the example of nested data I want to update the qty from it
let categoriesInfo = [
{
catid: 'category-1',
catName: 'Fun Stuff',
product: [
{
id : 1,
name: 'candy',
qty: 0,
},
{
id : 2,
name: 'cookie',
qty: 0,
}
]
},
{
catid: 'category-2',
catName: 'Fun Stuff2',
product: [
{
id : 1,
name: 'candy2',
qty: 0,
},
{
id : 2,
name: 'cookie2',
qty: 0,
}
]
}
]
I am using useState
const [productData, setProductData] = useState(categoriesInfo);
I am fetching the id and catid on click button from other component in the function x
const x = (data) =>{
console.log(productData)
const y = productData.map(obj => {
if (obj.catid == data.catid && obj.product[1-data.prodid].id == data.prodid) {
console.log(obj.product[1-data.prodid].name)
return {...obj.product[1-data.prodid], qty : 2}; //something like this
}
return obj;
});
setProductData(y);
}

Code looks really close, what about this?
const y = productData.map(obj => {
// you're not interested, just return obj
if (obj.catid !== data.catid) return obj;
// you are interested, return a new object
return {
...obj,
// iterate over it's products and update the one you want
product: obj.product.map(product => {
if(product.id !== data.prodid) return product
return { ...product, qty: 2}
})
}
});

Try like below:
let categoriesInfo = [ { catid: "category-1", catName: "Fun Stuff", product: [ { id: 1, name: "candy", qty: 0 }, { id: 2, name: "cookie", qty: 0 } ] }, { catid: "category-2", catName: "Fun Stuff2", product: [ { id: 1, name: "candy2", qty: 0 }, { id: 2, name: "cookie2", qty: 0 } ] } ];
function App() {
const [productData, setProductData] = React.useState(categoriesInfo);
const x = (data) => {
setProductData((prevProductData) =>
prevProductData.map((item) =>
item.catid === data.catid
? {
...item,
product: item.product.map((product) =>
product.id === data.prodid
? { ...product, qty: data.qty }
: product
)
}
: item
)
);
};
return (
<div>
<button onClick={() => x({ catid: "category-2", prodid: 2, qty: 4 })}>
Update value
</button>
<div>{JSON.stringify(productData, null, 4)}</div>
</div>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>

Related

Being able to remove duplicate keys from an array of objects

I have a question about how I can delete the existing elements, for example, in my case "Tallas" is repeated, could you please help me? Thank you very much to those who are willing to help me to solve this problem
const data =
[ { atributos: { Tallas: [{ id: 0, name: 'XS' }, { id: 1, name: 'S' }] }}
, { atributos: { Calzado: [{ id: 0, name: '10' }, { id: 1, name: '9.5' }] }}
, { atributos: { Tallas: [{ id: 0, name: 'XS' }] }}
]
The idea is to have this json format with the last "Tallas" since it is the last one that I added through my dynamic form.
const expected =
[{ atributos: { Calzado: [{ id: 0, name: '10' }, { id: 1, name: '9.5' }] }}
, { atributos: { Tallas: [{ id: 0, name: 'XS' }] }}
]
How do I do this is there a way to do it, I've tried with filter plus the findindex but I can't get to eliminate the repetition of the json res= new.filter((arr, index, self) => index === self.findIndex( (t) => (t.attributes === arr.attributes )))
To unique the array of objects, we can use the Javascript Set module, if the array has complex nested objects, we can stringify each object before creating new Set data. this below function will unique the array of complex objects.
function unique_array(array = []) {
const newSetData = new Set(array.map((e) => JSON.stringify(e)));
return Array.from(newSetData).map((e) => JSON.parse(e));
}
this is a function that takes an array and return the same array but delete every duplicated item
function removeDuplicates(arr) {
return arr.filter((item,
index) => arr.indexOf(item) === index);
}
I didn't understant the part written in spanish so I hope this is what you are looking for
This is a solution specific to your question. this is not a generic solution.
const data = [
{
atributos: {
Tallas: [
{ id: 0, name: "XS" },
{ id: 1, name: "S" },
],
},
},
{
atributos: {
Calzado: [
{ id: 0, name: "10" },
{ id: 1, name: "9.5" },
],
},
},
{
atributos: {
Tallas: [
{ id: 0, name: "XS" },
{ id: 1, name: "S" },
],
},
},
];
function uniqueArray(array) {
const resultObject = array.reduce((acc, eachValue) => {
let keys = Object.keys(eachValue.atributos);
keys.forEach((eachKey) => {
if (!acc[eachKey]) {
acc[eachKey] = [];
}
let list = eachValue["atributos"][eachKey].map(
(each) => each.id + "-" + each.name
);
acc[eachKey].push(...list);
});
return acc;
}, {});
const resultArray = Object.keys(resultObject).reduce((acc, each) => {
let setData = Array.from(new Set(resultObject[each]));
acc.push({
atributos: {
[each]: setData.map((e) => {
return { id: e.split("-")[0], name: e.split("-")[1] };
}),
},
});
return acc;
}, []);
return resultArray;
}
const result = uniqueArray(data)
console.log("result ", JSON.stringify(result, null, 2));

TS/JS, Unable to sum more than one properties, using map set and get

I have been trying to create a summary of an array of objects where it's grouped by the value of one property and 2 or more properties should get summed.
But for some reason the way I am trying is only giving me 2 values the property I am grouping by and first property I am summing.
I am unable to sum the next property.
The array I am starting with
combinedItems
[
{
itemSi: 1,
productId: 'one',
taxableValue: 100,
taxValue: 10,
product: { id: 'one', productName: 'product one', taxId: 'one' },
tax: { id: 'one', taxName: 'tax one' }
},
{
itemSi: 2,
productId: 'two',
taxableValue: 100,
taxValue: 10,
product: { id: 'two', productName: 'product two', taxId: 'one' },
tax: { id: 'one', taxName: 'tax one' }
}
]
I need to be able to group by the taxName and sum the taxableValue and taxValue.
const summaryValues = new Map<any []>();
for(const {tax, taxableValue, taxValue} of combinedItems)
summaryValues.set(
tax.taxName,
(summaryValues.get(tax.taxName) || 0) + taxableValue,
(summaryValues.get(tax.taxName) || 0) + taxValue,
);
const summaries = [...summaryValues]
console.log(summaries);
const taxSummary = summaries.map(x => ({
taxName: x[0],
taxableValue: x[1],
taxValue: x[2]
}));
console.log(taxSummary)
The result I am getting
[ [ 'tax one', 200 ] ]
[ { taxName: 'tax one', taxableValue: 200, taxValue: undefined } ]
This is how the combined items are gotten:
const items: any[] = [
{
itemSi: 1,
productId: "one",
taxableValue: 100,
taxValue: 10
},
{
itemSi: 2,
productId: "two",
taxableValue: 100,
taxValue: 10
}
];
const products: any[] = [
{
id: "one",
productName:"product one",
taxId: "one"
},
{
id: "two",
productName:"product two",
taxId: "one"
}
]
const taxes: any[] = [
{
id: "one",
taxName:"tax one"
},
{
id: "two",
taxName:"tax two"
}
]
let combinedItems: any [] = []
combinedItems = items.map(x => {
let pdtItem = products.find(z => z.id === x.productId);
let taxItem = taxes.find(z => z.id === pdtItem.taxId);
let item = {...x, product: {...pdtItem }, tax: {...taxItem}};
return item;
});
console.log(combinedItems)
Map is a key-value store. What you're trying to do appears to be calling set with three arguments, whereas it only takes two (key and value).
If you need to produce multiple aggregations, you could store the results in an object:
const summaries = new Map();
for (const { tax: { taxName }, taxableValue, taxValue } of combinedItems) {
const currentSummary = summaries.get(taxName) || { taxableValue: 0, taxValue: 0 }
summaries.set(
taxName,
{ taxableValue: currentSummary.taxableValue + taxableValue, taxValue: currentSummary.taxValue + taxValue }
);
}

How to sum each value inside array of object to new variable

I have a data like this :
const fund =
[
{
id: 1234,
totalAmount: 0,
data:
[
{
id: 1234,
amount: '4.000'
},
{
id: 1234,
amount: '3.000'
}
]
},
{
id: 12345,
totalAmount: 0
},
{
id: 123456,
totalAmount: 0
data:
[
{
id: 123456,
amount: '3.000'
},
{
id: 123456,
amount: '5.000'
}
]
}
]
I want to sum the amount inside of data each id to a key called totalAmount. But not all the parent id have data key.
here's my desired output :
const fund =
[
{
id: 1234
data:
[
{
id: 1234,
amount: '4.000'
},
{
id: 1234,
amount: '3.000'
}
],
totalAmount: 7000
},
{
id: 12345,
totalAmount: 0
},
{
id: 123456,
data:
[
{
id: 123456,
amount: '3.000'
},
{
id: 123456,
amount: '5.000'
}
],
totalAmount: 8000
}
]
I was trying with this code :
fund.forEach((elA, i) => {
if (elA.data) {
const total = funders[i].data.reduce((acc, curr) => {
acc += parseInt(curr.amount.replace(/\./g, ''))
return acc
})
fund[i] = total ? {...elA, totalAmount: total} : elA;
}
})
But it's not summing like i want.
Where's my mistake ?
Please ask me if you need more information if it's still not enough to solve that case.
You need to define the initial value for the reduce iterator.
fund.forEach((elA, i) => {
if (elA.data) {
const total = funders[i].data.reduce((acc, curr) => {
acc += parseInt(curr.amount.replace(/\./g, ''))
return acc
}, 0)
fund[i] = total ? {...elA, totalAmount: total} : elA;
}
});
Another alternative for the same code:
fund.forEach(elA => {
if (elA.data) {
const total = elA.data.reduce((acc, curr) => {
return acc + parseInt(curr.amount.replace(/\./g, ''))
}, 0)
elA.totalAmount = total;
}
});

How to Group JavaScript Array of Object based on key

So I have a data like this
const carts = [
{
name: 'Voucher A',
participants: [
{
date: 112
},
{
date: 112
}
],
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
},
{
name: 'Voucher B',
participants: [
{
date: 111
},
{
date: 112
}
],
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
}
]
And I want to group it based on the date (if it has same date). So for data above, the expected result will be
expected = [
{
name: 'Voucher A',
date: 1,
count: 1,
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
},
{
name: 'Voucher A',
date: 2,
count: 1,
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
}
]
Because it has different date. But if it has same date, the expected result will be
expected = [
{
name: 'Voucher A',
date: 1,
count: 2,
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
}
]
I was trying to use reduce to group it but it did not give the structure I want
carts.forEach(cart => {
cart.participants.reduce((acc, obj) => {
acc[obj.date] = [...acc[obj.date] || [], obj]
return acc
}, {})
})
To organize the data, I think you need two associations to group by: the name and the dates and their counts for that name:
const carts = [
{
name: 'Voucher A',
participants: [
{
date: 1
},
{
date: 2
}
]
}
];
const groupedByNames = {};
for (const { name, participants } of carts) {
if (!groupedByNames[name]) groupedByNames[name] = {};
for (const { date } of participants) {
groupedByNames[name][date] = (groupedByNames[name][date] || 0) + 1;
}
}
const output = Object.entries(groupedByNames).flatMap(
([name, dateCounts]) => Object.entries(dateCounts).map(
([date, count]) => ({ name, date: Number(date), count })
)
);
console.log(output);
If you want use, just plain for loops, you can try this solution. It looks simple and elegant 😜😜
const carts = [
{
name: 'Voucher A',
participants: [
{
date: 1
},
{
date: 1
},
{
date: 2
}
]
},
{
name: 'Voucher B',
participants: [
{
date: 1
},
{
date: 2
},
{
date: 2
}
]
}
]
const finalOutput = []
for (const cart of carts) {
for (const participant of cart.participants) {
const res = finalOutput.find(e => e.name === cart.name && e.date === participant.date)
if (res) {
res.count += 1
} else {
finalOutput.push({ name: cart.name, date: participant.date, count: 1 })
}
}
}
console.log(finalOutput)
Use forEach and destructuring
const process = ({ participants, name }) => {
const res = {};
participants.forEach(({ date }) => {
res[date] ??= { name, count: 0, date };
res[date].count += 1;
});
return Object.values(res);
};
const carts = [
{
name: "Voucher A",
participants: [
{
date: 1,
},
{
date: 2,
},
],
},
];
console.log(carts.flatMap(process));
const carts2 = [
{
name: "Voucher A",
participants: [
{
date: 1,
},
{
date: 1,
},
],
},
];
console.log(carts2.flatMap(process));

What is an alternative way to update an array of objects?

I have a array of objects. I want to update an object using id.
I am able to do using the map function. Is there an alternative way or more efficient way to update the array?
Here is my code:
https://stackblitz.com/edit/js-xgfwdw?file=index.js
var id = 3
var obj = {
name: "test"
}
let arr = [{
name: "dd",
id: 1
}, {
name: "dzxcd",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
function getUpdated(obj, id) {
var item = [...arr];
const t = item.map((i) => {
if(i.id==id){
return {
...obj,
id
}
}else {
return i;
}
})
return t
}
console.log(getUpdated(obj,id))
The expected output is correct but I want to achieve the same functionality using an alternative way.
[{
name: "dd",
id: 1
}, {
name: "test",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
you are in the correct way, basically the bad thing that you are doing is creating new arrays [...arr], when map already gives you a new array.
other things to use, may be the ternary operator and return directly the result of the map function
check here the improvedGetUpdate:
var id = 3;
var obj = {
name: "test"
};
let arr = [{
name: "dd",
id: 1
}, {
name: "dzxcd",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
function getUpdated(obj, id) {
var item = [...arr];
const t = item.map((i) => {
if (i.id == id) {
return {
...obj,
id
}
} else {
return i;
}
})
return t
}
improvedGetUpdate = (obj, id) => arr.map(i => {
return i.id !== id ? i : {
...obj,
id
}
})
console.log(getUpdated(obj, id))
console.log(improvedGetUpdate(obj, id))
var id = 3
var obj = {
name: "test"
}
let arr = [{
name: "dd",
id: 1
}, {
name: "dzxcd",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
const result = arr.map((el) => el.id === id ? {...obj, id} : el)
console.log(result);
Use splice method which can be used to update the array too:
var obj = {
id: 3,
name: "test"
}
let arr = [{
name: "dd",
id: 1
}, {
name: "dzxcd",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
arr.splice(arr.findIndex(({id}) => id === obj.id), 0, obj);
console.log(arr);
#quirimmo suggested short code.
I suggest fast code.
var id = 3;
var obj = {
id: 3,
name: "test"
}
let arr = [{
name: "dd",
id: 1
}, {
name: "dzxcd",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
var arr2 = [...arr];
console.time('⏱');
arr.splice(arr.findIndex(({id}) => id === obj.id), 0, obj);
console.timeEnd('⏱');
console.time('⏱');
for (let item of arr2) {
if (item.id === id) {
item.name = obj.name;
break;
}
}
console.timeEnd('⏱');
console.log(arr2);

Categories

Resources