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)

1
  • Array.prototype.reduce(), Object.keys() and Array.prototype.map() are your friends in this quest. Commented Dec 13, 2016 at 15:53

6 Answers 6

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; }

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

Comments

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

Comments

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

Comments

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

Comments

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;
        }
    );

Comments

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

Comments

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.