2

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 :)

estellechvl
  • 335
  • 4
  • 14

6 Answers6

2

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))
adiga
  • 34,372
  • 9
  • 61
  • 83
1

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);
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • +1 thanks !! With the order being important in my case, this solution is very helpful. I'm not sure I quite understand it completely yet. I mean I get the reduce part. I've never used new Map though. I'll try to read on that. thanks again – estellechvl Jul 16 '19 at 14:58
  • 1
    You're welcome :) I'll add a link to info about Map as well. – Ori Drori Jul 16 '19 at 14:59
1

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);
prabhatojha
  • 1,925
  • 17
  • 30
0

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
}, []))
Kobe
  • 6,226
  • 1
  • 14
  • 35
0

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);
ttulka
  • 10,309
  • 7
  • 41
  • 52
0
        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;
        }
loki97
  • 1
  • 1