mg1075
mg1075

Reputation: 18155

how to efficiently merge two arrays of date objects using lodash

The first array is a master list of date objects containing unique javascript Date objects:

[
   {'date': dateobject1, 'value': null},
   {'date': dateobject2, 'value': null},
   {'date': dateobject3, 'value': null},
   etc...
]

The first array is a much smaller list of date objects containing a subset of the unique javascript Date objects, with the 'value' property always having a number rather than a null:

[
   {'date': dateobject3, 'value': 3117},
   {'date': dateobject8, 'value': 14},
   etc...
]

Keeping in mind the nuances of comparing date objects - https://stackoverflow.com/a/493018/538962 - what would be the most efficient way to merge these objects - in an environment where lodash 3.10.1 is available - based on matching dates so that the merged array is a list of all dates: matches mean the 'value' becomes the numeric value, and otherwise the null 'value' is retained when there is no match?

[
   {'date': dateobject1, 'value': null},
   {'date': dateobject2, 'value': null},
   {'date': dateobject3, 'value': 3117},
   {'date': dateobject4, 'value': null},
   {'date': dateobject5, 'value': null},
   {'date': dateobject6, 'value': null},
   {'date': dateobject7, 'value': null},
   {'date': dateobject8, 'value': 14},
   etc...
]

Upvotes: 4

Views: 2680

Answers (3)

RobG
RobG

Reputation: 147403

You can use the built-in Array.prototype.map to create a new array from the existing array values.

Where you say:

The first array is a much smaller list of date objects containing a subset of the unique javascript Date objects

I'm assuming that dateobject3 is the same object in both arrays, not two different objects for the same date.

The following creates an index for the values array to save iterating over values for each member of data. It won't make a noticeable difference for small data sets (say less than 100 values), but will for larger ones (say more than a few hundred or a thousand).

var d0 = new Date(2017,1,1),
    d1 = new Date(2017,1,2),
    d2 = new Date(2017,1,3);

var data = [
   {'date': d0, 'value': null},
   {'date': d1, 'value': null},
   {'date': d2, 'value': null}
];

var values = [
   {'date': d1, 'value': 3117},
];

// Generate a values index, saves searching
// through values for each member of data
var indexes = values.map(obj => obj.date);
// Generate merged array
var merged = data.map(obj => {
  var index = indexes.indexOf(obj.date); 
  return {date: obj.date, value: index > -1? values[index].value : obj.value};
});

console.log(merged)

If on the other hand the dates are different objects but with the same time value, then you need to compare the time value instead. A simple way to get the time value is to use unary +, it just saves typing over getValue or getTime:

var data = [
   {'date': new Date(2017,1,1), 'value': null},
   {'date': new Date(2017,1,2), 'value': null},
   {'date': new Date(2017,1,3), 'value': null}
];

var values = [
   {'date': new Date(2017,1,2), 'value': 3117},
];

// Generate a values index using the date time value
// saves searching through values for each member of data
var indexes = values.map(obj => +obj.date);
// Generate merged array
var merged = data.map(obj => {
  var index = indexes.indexOf(+obj.date); 
  return {date: obj.date, value: index > -1? values[index].value : obj.value};
});

console.log(merged)

If each date will only have one match, you can optimise further by removing matched indexes when they're matched so subsequent lookups are (possibly imperceptibly) faster.

Upvotes: 2

TimCodes
TimCodes

Reputation: 365

Think using a hashset would be one of the quicker ways https://codepen.io/TimCodes/pen/brQvbo?editors=0012

var masterListArr = [ { 'date' : new Date(), 'value' : 2 } , {'date' : new Date(2013, 13, 1), 'value' : 0}]
var secondListArr = [ { 'date' : new Date(), 'value' : null } , {'date' : new Date(2014, 13, 1), 'value' : null}]

var masterListHash = masterListArr.reduce(function(acc, cur, i) {
  acc[cur['date'].getTime()] = cur['value'] ;
  return acc;
}, {});


var mergedList = secondListArr.map(dateObj => {
   var dateKey = dateObj.date.getTime()
   if(masterListHash.hasOwnProperty(dateKey)){
        dateObj.value = masterListHash[dateKey]
   }
   return dateObj
})

Upvotes: 0

pizzarob
pizzarob

Reputation: 12029

Nice question. I would use the reduce method on the master list to see if the other list contains a matching date - if so merge the two objects and push to the new array, if not just push the object from the master list. Mind you this is using es6 - if es6 is not available to you in the environment you are using lodash has a reduce and assign method

const masterList = [/* ... */];
const otherArray = [/* ... */];

const mergedArray = masterList.reduce((a, b) => {
  const match = otherArray.filter(({ date }) => date.getTime() === b.date.getTime());
  if(match) {
    a.push(Object.assign(b, match));
  } else {
    a.push(b);
  }

  return a;
}, []);

Upvotes: 0

Related Questions