Search code examples
javascriptlodash

Merge objects with with missing key


I have two objects like that:

const object1 = {first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}], second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 2}]}
const object2 = {first: [{timestamp: "2018-12-09T16:00:00.000", count: 0}], second: [{timestamp: "2018-12-09T17:00:00.000", count: 0}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 3}]}

(object1 and object2 could have array of lenght > 1, for example:

const object1 = {first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}, {timestamp: "2018-12-09T16:30:00.000", count: 5}], second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 2}]}
const object2 = {first: [{timestamp: "2018-12-09T16:00:00.000", count: 0}, {timestamp: "2018-12-09T16:30:00.000", count: 0}], second: [{timestamp: "2018-12-09T17:00:00.000", count: 0}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 3}]}

)

and I want an object of the same format but:

  • the value of key timestamp should be a real data not a string (so I need to do new Data(timestamp))
  • the value of key count should be the sum

So the expected result is:

const res = {first: [{timestamp: 2018-12-09T16:00:00.000, count: 3}], second: [{timestamp: 2018-12-09T17:00:00.000, count: 2}], third: [{timestamp: 2018-12-09T18:00:00.000, count: 5}]}

( if object1 and object2 have array of length > 1:

const res = {first: [{timestamp: 2018-12-09T16:00:00.000, count: 3}, {timestamp: "2018-12-09T16:30:00.000", count: 5}], second: [{timestamp: 2018-12-09T17:00:00.000, count: 2}], third: [{timestamp: 2018-12-09T18:00:00.000, count: 5}]}

)

I used mergeWith of Lodash in this way:

const merged = _.mergeWith(object1, object2, (objValue, srcValue) => [
  { count: objValue[0].count + srcValue[0].count },
])
const r = Object.entries(merged).map(([key, value], i) => {
  return { number: key, timestamp: value.map(convertTimestamp) }
})
console.log('r: ', r)

where convertTimestamp is:

const convertTimestamp = (d) => ({
  ...d,
  timestamp: new Date(d.timestamp),
})

This is the result:

[
  {
    "number": "first",
    "timestamp": [
      {
        "count": 3,
        "timestamp": null
      }
    ]
  },
  {
    "number": "second",
    "timestamp": [
      {
        "count": 2,
        "timestamp": null
      }
    ]
  },
  {
    "number": "third",
    "timestamp": [
      {
        "count": 5,
        "timestamp": null
      }
    ]
  }
]

Obviously it doesnt' work. It has 3 problems:

  1. nested object with no correct values
  2. timestamp is not correct
  3. if object1 and object2 have the same key but If they are:

const object1 = {first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}], second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 2}]}

const object2 = {second: [{timestamp: "2018-12-09T17:00:00.000", count: 0}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 3}]}

(object2 is missing first), that procedure doesn't work...

I need help

Here a testable code:

function mergeData(object1, object2) {
  const merged = _.mergeWith(object1, object2, (objValue, srcValue) => [
    { count: objValue[0].count + srcValue[0].count},
  ])
  const r = Object.entries(merged).map(([key, value], i) => {
    return { number: key, timestamp: value.map(convertTimestamp) }
  })
  return r
}

const convertTimestamp = (d) => {
  return ({
    ...d,
    timestamp: new Date(d.timestamp),
  })
}

const object1 = {first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}], second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 2}]}
const object2 = {first: [{timestamp: "2018-12-09T16:00:00.000", count: 0}], second: [{timestamp: "2018-12-09T17:00:00.000", count: 0}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 3}]}


const result = mergeData(object1, object2)
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>


Some examples:

// example 1
const object1 = {
  first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}, {timestamp: "2018-12-09T16:30:00.000", count: 5}], 
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 2}]
}
const object2 = {
  first: [{timestamp: "2018-12-09T16:00:00.000", count: 0}, {timestamp: "2018-12-09T16:30:00.000", count: 0}], 
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 0}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 3}]
}

const result = {
  first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}, {timestamp: "2018-12-09T16:30:00.000", count: 5}], 
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 5}]
}


// example 2
const object1 = {
  first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}, {timestamp: "2018-12-09T16:30:00.000", count: 5}], 
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 2}]
}
const object2 = {
  first: [{timestamp: "2018-12-09T16:00:00.000", count: 0}], 
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 0}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 3}]
}

const result = {
  first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}, {timestamp: "2018-12-09T16:30:00.000", count: 5}], 
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 5}]
}


// example 3
const object1 = {
  first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}, {timestamp: "2018-12-09T16:30:00.000", count: 5}], 
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 2}]
}
const object2 = {
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 0}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 3}]
}

const result = {
  first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}, {timestamp: "2018-12-09T16:30:00.000", count: 5}], 
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 5}]
}


// example 4
const object1 = {
  first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}, {timestamp: "2018-12-09T16:30:00.000", count: 5}], 
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}, {timestamp: "2018-12-09T17:30:00.000", count: 20}, {timestamp: "2018-12-09T18:00:00.000", count: 10}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 2}]
}
const object2 = {
  first: [{timestamp: "2018-12-09T16:00:00.000", count: 0}, {timestamp: "2018-12-09T16:30:00.000", count: 0}], 
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 0}, {timestamp: "2018-12-09T17:30:00.000", count: 6}, {timestamp: "2018-12-09T18:00:00.000", count: 2}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 3}]
}

const result = {
  first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}, {timestamp: "2018-12-09T16:30:00.000", count: 5}], 
  second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}, {timestamp: "2018-12-09T17:30:00.000", count: 26}, {timestamp: "2018-12-09T18:00:00.000", count: 12}], 
  third: [{timestamp: "2018-12-09T18:00:00.000", count: 5}]
}

Solution

  • Lodash's _.mergeWith() callback gets the keys as the 3rd param. You can use it to decide how to merge the items:

    const object1 = {first: [{timestamp: "2018-12-09T16:00:00.000", count: 3}], second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 2}]}
    const object2 = {first: [{timestamp: "2018-12-09T16:00:00.000", count: 0}], second: [{timestamp: "2018-12-09T17:00:00.000", count: 0}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 3}]}
    
    const mergefn = (...args) =>
      _.mergeWith({}, ...args, (o, s, k) => {
          if(_.eq(k, 'timestamp')) return new Date(s);
          if(_.eq(k, 'count')) return (o || 0) + s;
      });
      
    const result = mergefn(object1, object2);
    
    console.log(result);
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>