3

I am trying to merge data on any duplicate key but also rewrite the data object.

I am trying to merge the array of values depending on if each object has the same 'time'. After which, I would like to pair each value within the items with the name.

I think the easiest way to show is through the raw data I hope to transform, So I would like to transform the following;

var data = [{
  "item": ["1", "2"],
  "time": "12-15",
  "name": "ben"
}, {
  "item": ["3", "4"],
  "time": "12-15",
  "name": "bill"
}, {
  "item": ["1", "2", "3"],
  "time": "15-18",
  "name": "ben"

}, {
  "item": ["4", "5", "6"],
  "time": "15-18",
  "name": "bill"
}];

Into

var result = [{
  "time": "12-15",
  "ben": ["1", "2"],
  "bill": ["3", "4"]
},
{
  "time": "15-18",
  "ben": ["1", "2", "3"],
  "bill": ["4", "5", "6"]
}]

I have been trying to this this question to help me do this however I'm not getting very far. I cannot seem to resolve the issue of the first item that is checked not being output as an array.

Any help is much appreciated!

var data = [{
  "item": ["1", "2"],
  "time": "12-15",
  "name": "ben"
}, {
  "item": ["3", "4"],
  "time": "12-15",
  "name": "bill"
}, {
  "item": ["1", "2", "3"],
  "time": "15-18",
  "name": "ben"

}, {
  "item": ["4", "5", "6"],
  "time": "15-18",
  "name": "bill"

}];



var seen = {};
var result = data.filter(function(entry) {
  var previous;

  // Have we seen this label before?
  if (seen.hasOwnProperty(entry.time)) {

    // Yes, grab it and add this data to it
    previous = seen[entry.time];
    previous.item.push(entry.item);

    // Don't keep this entry, we've merged it into the previous one
    return false;
  }
  //console.log(seen)
  // entry.data probably isn't an array; make it one for consistency
  if (!Array.isArray(entry.item)) {
    entry.item = [entry.item];
  }

  // Remember that we've seen it
  seen[entry.time] = entry;

  // Keep this one, we'll merge any others that match into it
  return true;
});

console.log(result)
Community
  • 1
  • 1
ggt
  • 331
  • 1
  • 11
  • `Array.prototype.reduce()`, `Object.keys()` and `Array.prototype.map()` are your friends in this quest. – Redu Dec 13 '16 at 15:53

6 Answers6

1

You could use a hash table for grouping.

var data = [{ "item": ["1", "2"], "time": "12-15", "name": "ben" }, { "item": ["3", "4"], "time": "12-15", "name": "bill" }, { "item": ["1", "2", "3"], "time": "15-18", "name": "ben" }, { "item": ["4", "5", "6"], "time": "15-18", "name": "bill" }],
    result = [];

data.forEach(function (a) {
    if (!this[a.time]) {
        this[a.time] = { time: a.time };
        result.push(this[a.time]);
    }
    this[a.time][a.name] = (this[a.time][a.name] || []).concat(a.item);
}, Object.create(null));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Or with ES6 you could use a Map.

var data = [{ "item": ["1", "2"], "time": "12-15", "name": "ben" }, { "item": ["3", "4"], "time": "12-15", "name": "bill" }, { "item": ["1", "2", "3"], "time": "15-18", "name": "ben" }, { "item": ["4", "5", "6"], "time": "15-18", "name": "bill" }],
    map = new Map,
    result = [];

data.forEach(a => {
    var o = map.get(a.time);
    if (!o) {
        o = { time: a.time };
        map.set(a.time, o);
        result.push(o);
    }
    o[a.name] = (o[a.name] || []).concat(a.item);
});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

I would like to follow this approach creating two functions and returning a new object with the merged data, this way you avoid the mutation of your original object.

Note: this uses ES6 syntax but you can easily transform this code into ES5.

const data = [{
  "item": ["1", "2"],
  "time": "12-15",
  "name": "ben"
}, {
  "item": ["3", "4"],
  "time": "12-15",
  "name": "bill"
}, {
  "item": ["1", "2", "3"],
  "time": "15-18",
  "name": "ben"

}, {
  "item": ["4", "5", "6"],
  "time": "15-18",
  "name": "bill"

}];

// Get a list of unique times
const getTimes = data => data.reduce((a, c) => {
  if (!a.includes(c.time)) {
    a.push(c.time);
  }      
  
  return a;
}, []);

// Merge the data into a single list using the times list as index
const mergeData = (data, times) => times.map(time => {
  const obj = {};
  obj.time = time;
  
  data.forEach(record => {
    if (record.time === time) {
      obj[record.name] = record.item;
    }
  });
  
  return obj;
});

const times = getTimes(data);
const result = mergeData(data, times);

console.log(result);
David Gomez
  • 2,762
  • 2
  • 18
  • 28
0

It might be helpful to write a function to group stuff by time:

class DefaultMap extends Map {
    constructor(defaultConstructor) {
        super();
        this.defaultConstructor = defaultConstructor;
    }

    get(key) {
        if (this.has(key)) {
            return super.get(key);
        }

        const def = this.defaultConstructor();
        this.set(key, def);
        return def;
    }
}

function groupBy(collection, key) {
    const groups = new DefaultMap(() => []);

    for (const item of collection) {
        const itemKey = key(item);
        const group = groups.get(itemKey);

        group.push(item);
    }

    return groups;
}

Then you can just:

const result =
    groupBy(data, entry => entry.time).entries().map(
        ([time, entries]) => {
            const group = {time};

            entries.forEach(entry => {
                group[entry.name] = entry.item;
            });

            return group;
        }
    );
Ry-
  • 218,210
  • 55
  • 464
  • 476
0

You can do it just with map() and filter() plus an inner loop like this :

var data = [{
  "item": ["1", "2"],
  "time": "12-15",
  "name": "ben"
}, {
  "item": ["3", "4"],
  "time": "12-15",
  "name": "bill"
}, {
  "item": ["1", "2", "3"],
  "time": "15-18",
  "name": "ben"
}, {
  "item": ["4", "5", "6"],
  "time": "15-18",
  "name": "bill"
}];

var skel = data.map(x => x.time).filter((x,i,arr) => arr.indexOf(x) === i).map(x => ({"time" : x}));
var result = skel.map(x => {
  data.forEach(y => {
    if(x.time === y.time)
      x[y.name] = y.item;
  })
  return x;
} )
console.log(result);
kevin ternet
  • 4,514
  • 2
  • 19
  • 27
0

You may do as follows;

var data = [{
  "item": ["1", "2"],
  "time": "12-15",
  "name": "ben"
}, {
  "item": ["3", "4"],
  "time": "12-15",
  "name": "bill"
}, {
  "item": ["1", "2", "3"],
  "time": "15-18",
  "name": "ben"

}, {
  "item": ["4", "5", "6"],
  "time": "15-18",
  "name": "bill"
}],

interim = data.reduce((h,d) => (h[d.time] = h[d.time] ? h[d.time].concat({[d.name]: d.item})
                                                      : [{[d.name]: d.item}],h),{}),
 result = Object.keys(interim)
                .map(k => Object.assign({time: k},...interim[k]));
console.log(result);
Redu
  • 25,060
  • 6
  • 56
  • 76
0

Try this

var timeGroup = _.groupBy(data,"time");

_.mapObject(timeGroup,function(val,key){
var benArray = _.flatten(_.values(_.pick(_.findWhere(val,   {name:"ben"}),"item")));
var billArray = _.flatten(_.values(_.pick(_.findWhere(val,{name:"bill"}),"item")));
console.log({"time" : key,"ben" : benArray , "bill" : billArray })
})
Vignesh Murugan
  • 575
  • 5
  • 18