4

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...
]
mg1075
  • 17,985
  • 8
  • 59
  • 100

3 Answers3

2

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.

RobG
  • 142,382
  • 31
  • 172
  • 209
0

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;
}, []);
pizzarob
  • 11,711
  • 6
  • 48
  • 69
  • 1
    This mutates the members of *masterList* at `Object.assign(b, match)`, so on completion *mergedArray* is a set of references to the elements in the mutated *masterList*. So you might as well use *forEach* instead of *reduce* and not bother with *mergedArray*. ;-) – RobG Aug 30 '17 at 01:15
0

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
})
TimCodes
  • 365
  • 2
  • 14
  • Using the time value as the key raises the question of duplicate dates. Also, you're building a plain object, not a hash. The distinction may be subtle, but ECMAScript may get real hashes in the future (e.g. it recently acquired Map and Set objects). You could create a Map and use the Date object itself as the key (if the dates in the two arrays are the same object). Dunno if that's what the OP meant by "*…containing a subset of the unique javascript Date objects*" though. – RobG Aug 30 '17 at 01:06