1

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

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I have one more question? What if I have more object values except id and qty, example: I have object like {id: 1, name: "xxx", qty: 2, checked: true}, how can name value copy to arr2 too.
Change the qtyById object so that its values are not just the quantity, but an object containing the quantity and name. This'll require an extra statement at the beginning of a loop iteration to create the object if it doesn't exist yet. qtyById[item.id] ??= { id, qty, name };
1

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.

1 Comment

Please do not use the above solution if the array sizes are significantly high. This solution employs ".find()" within an iteration - which is not optimal. Please refer to CertainPerformance's this solution which employs an object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.