Merge two arrays of dictionaries in javascript

Viewed 5594

I have two arrays of dictionaries which look something like this:

var lat = [{key:"2017-09-20T11:51:32.000Z", value:50.7825333},{key:"2017-09-20T11:51:33.000Z", value:50.7826},...];
var lon = [{key:"2017-09-20T11:51:32.000Z", value:-1.3075833},{key:"2017-09-20T11:51:33.000Z", value:-1.3076},...];

You might have guessed that one is an array of latitudes and one of longitudes!

I would like an elegant way of merging time, lat, lon into one array. Both arrays contain the same keys (I should check that this is always the case!).

var latLon = [{time:"2017-09-20T11:51:32.000Z", lat:50.7825333, lon:-1.3075833},...]

I have thrown something together that works but isn't pretty (ie iterate both arrays and append to a new one) but it feels like there must be a more stylish way using Object.assign with some nice lamdas. I am also using the D3.js library if that contains any useful methods.

3 Answers

You can use Array#map method to generate the new array( assuming both arrays are in the same order ).

var lat = [{key:"2017-09-20T11:51:32.000Z", value:50.7825333},{key:"2017-09-20T11:51:33.000Z", value:50.7826}];
var lon = [{key:"2017-09-20T11:51:32.000Z", value:-1.3075833},{key:"2017-09-20T11:51:33.000Z", value:-1.3076}];


var res = lat
  // iterate over the first array
  .map(function(o, i) {
    // generate the array element
    // where get values from element and
    // get value from second array using
    // the index
    return {
      time: o.key,
      lat: o.value,
      lon: lon[i].value
    }
  })

console.log(res);

// with ES6 arrow function
var res1 = lat.map((o, i) => ({time: o.key, lat: o.value, lon: lon[i].value}))


console.log(res1);


FYI : In case related array elements are not in same order then you need to get the element from the second array by comparing time value(you can use Array#find method) or you can generate a hashmap to map the object.

With Array#find method :

var lat = [{key:"2017-09-20T11:51:32.000Z", value:50.7825333},{key:"2017-09-20T11:51:33.000Z", value:50.7826}];
var lon = [{key:"2017-09-20T11:51:32.000Z", value:-1.3075833},{key:"2017-09-20T11:51:33.000Z", value:-1.3076}];



var res = lat
  .map(function(o) {
    return {
      time: o.key,
      lat: o.value,
      // get object by using find method
      lon: lon.find(function(o1) {
        return o1.key === o.key;
      }).value
    }
  })

console.log(res);

// with ES6 arrow function
var res1 = lat.map(o => ({
  time: o.key,
  lat: o.value,
  lon: lon.find(o1 => o1.key === o.key).value
}))

console.log(res1);

More efficient approach using a hashmap for referencing:

var lat = [{key:"2017-09-20T11:51:32.000Z", value:50.7825333},{key:"2017-09-20T11:51:33.000Z", value:50.7826}];
var lon = [{key:"2017-09-20T11:51:32.000Z", value:-1.3075833},{key:"2017-09-20T11:51:33.000Z", value:-1.3076}];

// generate reference hashmap for getting 
// value using the datetime string
var ref = lon.reduce(function(obj, o) {
  // set reference
  obj[o.key] = o.value;
  // return the reference object
  return obj;
  // set initial value as an empty object
}, {});

var res = lat
  .map(function(o) {
    return {
      time: o.key,
      lat: o.value,
      // get value from generated reference object
      lon: ref[o.key]
    }
  })

console.log(res);

I would avoid using find() if there's any chance you will be dealing will a lot of items. find() needs to search through the second array for every item in lat which is not efficient.

You can do this with a single map and reduce if you build a dictionary on the first loop and make the new array on the second.

This also allows the lat and lon arrays to be in different orders.

var lat = [{key:"2017-09-20T11:51:32.000Z", value:50.7825333},{key:"2017-09-20T11:51:33.000Z", value:50.7826}];
var lon = [{key:"2017-09-20T11:51:32.000Z", value:-1.3075833},{key:"2017-09-20T11:51:33.000Z", value:-1.3076}];

var obj = lat.reduce((a, c) => (a[c.key] = {time: c.key, lat:c.value}, a), {});
var arr = lon.map(lon => Object.assign(obj[lon.key], {lon: lon.value}));

console.log(arr);

This is based on the key time. Assuming there are no keys/times the same then you can do this:

var lat = [{key:"2017-09-20T11:51:32.000Z", value:50.7825333},{key:"2017-09-20T11:51:33.000Z", value:50.7826}];
var lon = [{key:"2017-09-20T11:51:32.000Z", value:-1.3075833},{key:"2017-09-20T11:51:33.000Z", value:-1.3076}];

var obj = {}, arr = lat.concat(lon);

arr.forEach(function(x){
    obj[x.key] ? obj[x.key]["lon"] = x.value : obj[x.key] = {lat: x.value, time: x.key};
});

var latlon = Object.keys(obj).map(function(x){
    return obj[x];
});

You are concatenating the 2 arrays to make one array. Then you are creating a dictionary using the time as a key (this is where it assumes that you do not have two lats and two longs with the same times). Since you know you are first getting the lats, your next key match should be the lons.

Related