estellechvl
estellechvl

Reputation: 335

Array Manipulation: merge objects [ES6]

So i've been struggling for a few hours now. Here's what I'm trying to do.

I have an array of objects:

initArr = [
  {id: 5, time: 100, download: true},
  {id: 2, time: 50, download: false},
  {id: 3, time: 1000, download: true},
  {id: 5, time: 50, download: true},
  {id: 5, time: 550, download: false},
  {id: 2, time: 1500, download: true}
]

some objects have the same id, I want to merge them together.
- but when merging i want to sum the time value of both merged object .
- if one of the two merged objects has download: false, the merged object should have false, otherwise true.

Here's what I have so far (I did not start yet even considering the download key):

const mergedArr= [];

initArr.map(obj => obj['id'])
     .map((id, i, arr) => {
        if (arr.indexOf(id) === i) {
            mergedArr.push(initArr[i]);
        } else { 
          const mergeIndex = mergedArr.map( x => x.id).indexOf(id);
          mergedArr[mergeIndex].playTime +=initArr[arr.indexOf(id)].playTime;
        }
    return mergedArr
});

I'd love so inputs or hints :)

Upvotes: 2

Views: 114

Answers (6)

loki97
loki97

Reputation: 1

        function manipulation(initArr){
            var resultArray = [];
            var arrMap = new Map();
            var currIndex = 0;
            initArr.forEach(function(item, index){
                if(arrMap.has(item.id)){
                    var existingItem = resultArray[arrMap.get(item.id)];
                    existingItem.time = item.time+existingItem.time;
                    if(existingItem.download && !item.download){
                        existingItem.download = false;
                    }
                }else{
                    arrMap.set(item.id, currIndex);
                    resultArray.push(item);
                    currIndex++;
                }
            })
            return resultArray;
        }

Upvotes: 0

Ori Drori
Ori Drori

Reputation: 191976

Reduce the array to a Map by the object's id, and then convert back to array using Map.values() and Array.from() (or by spreading into an array).

Note: in this case using Map as an accumulator is better than using an object, since the id property is numeric. In ES6 objects usually maintain the order of insertion, unless the properties are numeric. When an object with numeric property keys is convert to an array, the numeric properties come first, and they are ordered by their value. On the other hand, a Map always maintains the order of insertion.

const initArr = [
  {id: 5, time: 100, download: true},
  {id: 2, time: 50, download: false},
  {id: 3, time: 1000, download: true},
  {id: 5, time: 50, download: true},
  {id: 5, time: 550, download: false},
  {id: 2, time: 1500, download: true}
]

const result = Array.from(initArr.reduce((r, o) => {
  if(!r.has(o.id)) r.set(o.id, { ...o });
  else {
    const current = r.get(o.id);
    current.time += o.time;
    current.download = current.download && o.download
  }
  
  return r;
}, new Map).values());

console.log(result);

Upvotes: 1

prabhatojha
prabhatojha

Reputation: 2085

Try this, supports download option as well

const arr = [
  {id: 5, time: 100, download: true},
  {id: 2, time: 50, download: false},
  {id: 3, time: 1000, download: true},
  {id: 5, time: 50, download: true},
  {id: 5, time: 550, download: false},
  {id: 2, time: 1500, download: true}
]

const result = arr.reduce((res, obj) => {
    const found = res.find(t => t.id === obj.id);

    if(found){
        found.time += obj.time;
        found.download = found.download && obj.download;    
    } else {
        res.push(obj);
    }
    return res;
}, []);

console.log(result);

Upvotes: 1

adiga
adiga

Reputation: 35222

You could reduce the array. Create an accumulator object with each id as key and the merged object as value. If the id already exists, update time and download with current object. Else, add the current id as key and set a copy of the current object as it's value. Then use Object.values() to get an array of values from this accumulator object.

const initArr = [
  { id: 5, time: 100, download: true },
  { id: 2, time: 50, download: false },
  { id: 3, time: 1000, download: true },
  { id: 5, time: 50, download: true },
  { id: 5, time: 550, download: false },
  { id: 2, time: 1500, download: true }
]

const merged = initArr.reduce((acc, o) => {
  if (acc[o.id]) {
    acc[o.id].time += o.time;
    acc[o.id].download = acc[o.id].download && o.download;
  } else
    acc[o.id] = { ...o };

  return acc;
}, {})

console.log(Object.values(merged))

Upvotes: 2

ttulka
ttulka

Reputation: 10882

One more reduce solution:

const initArr = [
  {id: 5, time: 100, download: true},
  {id: 2, time: 50, download: false},
  {id: 3, time: 1000, download: true},
  {id: 5, time: 50, download: true},
  {id: 5, time: 550, download: false},
  {id: 2, time: 1500, download: true}
]

const merged = initArr.reduce((a,c) => {
    const found = a.find(x => x.id === c.id);
    if (found) {
        found.time += c.time;
        found.download = found.download && c.download;
        return a;
    }
    return [...a, {...c}];
}, []);

console.log(merged);

Upvotes: 0

Kobe
Kobe

Reputation: 6446

You can use reduce to:

  • check if the array has the item with the ID on each loop
  • if it does, add the time

else

  • push the object

const arr = [
  {id: 5, time: 100, download: true},
  {id: 2, time: 50, download: false},
  {id: 3, time: 1000, download: true},
  {id: 5, time: 50, download: true},
  {id: 5, time: 550, download: false},
  {id: 2, time: 1500, download: true}
]

console.log(arr.reduce((a, o) => {
  const i = a.findIndex(({id}) => id === o.id)
  i + 1 ? a[i].time += o.time : a.push(o)
  return a
}, []))

Upvotes: 0

Related Questions