I have 2 arrays like this:
const arr1 = [
{id: 1, qty: 2, checked: true},
{id: 2, qty: 2, checked: true},
{id: 3, qty: 2, checked: false}
]
const arr2 = [
{id: 1, qty: 2},
{id: 2, qty: 2}
]
I want to copy values of arr1
to arr2
where checked
is true
only, more than that if arr1
value is already exist in arr2
I just want it to update its qty
not duplicate
again. But my problem is that in some scenarios I got it duplicate and update at the same time. Below here I try with for loops.
const handleAdd = () => {
let newArray = [...arr2]
for (let i = 0; i < arr1.length; i ) {
for (let j = 0; j < arr2.length; j ) {
if(arr2[j].id === arr1[i].id && arr1[i].checked === true){
newArray[j].qty = newArray[j].qty arr1[i].qty
break
}else{
if(arr1[i].checked === true){
newArray.push(arr1[i])
break
}
}
}
}
console.log(newArray)
}
What went wrong here, any solution? Thanks in advance
CodePudding user response:
Here:
}else {
if (arr1[i].checked === true) {
newArray.push(arr1[i])
break
You're pushing to newArray
if an item is checked before iterating all the way through the second array. If the first array's item is checked, then no matter what's in the second array, you will only check what's in arr2[0]
before breaking in one of the branches - which will screw up your logic.
How about grouping up the second array by ID first? It'll make more sense at a glance and will also reduce the computational complexity.
const arr1 = [
{id: 1, qty: 2, checked: true},
{id: 2, qty: 2, checked: true},
{id: 3, qty: 2, checked: false}
]
const arr2 = [
{id: 1, qty: 2},
{id: 2, qty: 2}
]
const handleAdd = () => {
const qtyById = Object.fromEntries(
arr2.map(({ id, qty }) => [id, qty])
);
for (const item of arr1) {
if (item.checked) {
qtyById[item.id] = (qtyById[item.id] || 0) item.qty;
}
}
const newArray = Object.entries(qtyById).map(([id, qty]) => ({ id, qty }));
console.log(newArray)
}
handleAdd();
If the order of arr2
must be preserved and the items may not be in ascending numerical order, use a Map instead of an object so that the order of IDs is preserved in insertion order.
CodePudding user response:
Presented below is one possible way to achieve the desired objective.
Code Snippet
const myAdd = (needle, hayStack) => (
hayStack.map(
({id, qty}) => ({
id, qty,
...(
needle.some(n => n.checked && n.id === id)
? (
{ qty } = needle.find(n => n.checked && n.id === id),
{ qty }
)
: {}
)
})
).concat(
needle
.filter(
({ id, checked }) => checked && !hayStack.some(h => h.id === id)
).map(({ id, qty }) => ({ id, qty }))
)
);
/* explanation
// method to add or update arr2
const myAdd = (needle, hayStack) => (
// first iterate over "arr2" (called hayStack here)
// and update each item by matching "id" when "arr1" (called needle here)
// has "checked" true
hayStack.map(
// de-structure "arr2" to directly access "id" and "qty"
({id, qty}) => ({
id, qty, // by default populate both "id" and "qty"
...( // if "arr1" has a matching "id" and "checked" is true
// then, ".find()" the particular elt and
// update the "qty"
needle.some(n => n.checked && n.id === id)
? ( // extract only the "qty" from result ".find()"
{ qty } = needle.find(n => n.checked && n.id === id),
{ qty } // return an object with one prop "qty"
)
: {} // if "arr1" has no matching "id", no change to "arr2" elt
)
})
).concat(
// concat items in "arr1" which are not already present in "arr2"
// and have "checked" as "true"
needle
.filter( // first filter only "new" items
({ id, checked }) => checked && !hayStack.some(h => h.id === id)
).map( // destructure to extract only "id" and "qty" props
({ id, qty }) => ({ id, qty })
)
)
);
*/
const arr1 = [
{id: 1, qty: 3, checked: true},
{id: 2, qty: 2, checked: true},
{id: 3, qty: 2, checked: false},
{id: 4, qty: 4, checked: true}
];
const arr2 = [
{id: 1, qty: 2},
{id: 2, qty: 2}
];
console.log(
'add/update elements from arr1:\n',
JSON.stringify(arr1),
'\n\ninto arr2: ',
JSON.stringify(arr2),
'\n\n',
myAdd(arr1, arr2)
);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added to the snippet above.