javascriptarraysforeachlogicjavascript-objects

merge javascript objects in array


I'm trying to merge an array of object by removing the ones which have the same code attribute and computing its times attribute.

given the following array:

let itemsTimes = [
    {
        item: {
            code: '0.05'
        },
        times: 1
    },
    {
        item: {
            code: '0.01'
        },
        times: 1
    },
    {
        item: {
            code: '0.02'
        },
        times: 1
    },
    {
        item: {
            code: '0.01'
        },
        times: 1
    },
    {
        item: {
            code: '0.02'
        },
        times: 2
    }
];

and the following code:

let mergedItemTimes = [];

itemsTimes.forEach((itemTime, i) => {
    let j = i + 1
    if ((itemsTimes[j])) {
        itemsTimes.forEach((_, j) => {
            if (itemTime.item.code === itemsTimes[j].item.code) {
                itemTime.times += itemsTimes[j].times
                itemsTimes.splice(j, 1)
            }
        })
    }
    mergedItemTimes.push(itemTime)
})

The expected output it'll be:

[
    { item: { code: '0.05' }, times: 1 },
    { item: { code: '0.01' }, times: 2 },
    { item: { code: '0.02' }, times: 3 },
]

but it's giving me:

[
    { item: { code: '0.05' }, times: 2 },
    { item: { code: '0.02' }, times: 4 },
]

Thanks in advance.


Solution

  • As you call splice on an array you are iterating, that loop will not visit the next element, because that element shifted to the previous index, and the iteration continues at the next index.

    One of the common ways to do this, is with reduce -- not mutating the original objects or array:

    const itemsTimes = [{item: {code: '0.05'},times: 1},{item: {code: '0.01'},times: 1},{item: {code: '0.02'},times: 1},{item: {code: '0.01'},times: 1},{item: {code: '0.02'},times: 2}];
    const mergedItemTimes = Object.values(
        itemsTimes.reduce((acc, { item: { code }, times }) => {
            (acc[code] ??= { item: { code }, times: 0 }).times += times;
            return acc;
        }, {})
    );
    console.log(mergedItemTimes);