合并缺少键的对象

时间:2018-12-11 04:05:30

标签: javascript lodash

我有两个这样的对象:

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

({object1object2的长度可能大于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}]}

我想要一个相同格式的对象,但是:

  • timestamp的值应该是真实数据,而不是字符串(所以我需要做new Data(timestamp)
  • count的值应为总和

所以预期结果是:

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

(如果object1object2的数组长度> 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}]}

我以这种方式使用了Lodash的mergeWith

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)

其中convertTimestamp是:

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

这是结果:

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

显然,它不起作用。 它有3个问题:

  1. 没有正确值的嵌套对象
  2. 时间戳不正确
  3. 如果object1object2具有相同的密钥,但是如果它们相同:

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缺少first),该过程不起作用...

我需要帮助

以下是可测试的代码:

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/lodash@4.17.11/lodash.min.js"></script>


一些例子:

// 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}]
}

4 个答案:

答案 0 :(得分:0)

您可以在 ES6 中通过Array.reduceObject.entriesArray.forEach进行操作:

Note in the example I have extra objects in "first" array for tesitng purposes

const o1 = {first: [{timestamp: "2018-12-09T16:00:00.000", count: 3},{timestamp: "2019-12-09T16:00:00.000", count: 1}], second: [{timestamp: "2018-12-09T17:00:00.000", count: 2}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 2}]}
const o2 = {first: [{timestamp: "2018-12-09T16:00:00.000", count: 0}, {timestamp: "2019-12-09T16:00:00.000", count: 11}], second: [{timestamp: "2018-12-09T17:00:00.000", count: 0}], third: [{timestamp: "2018-12-09T18:00:00.000", count: 3}]}

const groupObjects = arr => arr.reduce((r,c) => {
  Object.entries(c).forEach(([k,v]) => {
    let key = `${k}`
    r[key] = r[key] || []
    v.forEach(({timestamp, count}, i) => {
      r[key][i] = r[key][i] || ({timestamp: new Date(timestamp), count: 0})
      r[key][i].count += count
    })
  })
  return r
}, {})

console.log(groupObjects([o1, o2]))

唯一棘手的事情是,您需要对对象键first进行分组,然后对每个对象执行第二次操作以获取总和。

这是假设object1object2始终具有相同的数组长度

答案 1 :(得分:0)

所以mergeWith所执行的操作是递归地应用定制器,所以您要检查的是类型,例如typeof(a) == 'string',如果对象中将有另一种类型的字符串数据,则必须添加更复杂的逻辑,以确保您使用正则表达式合并具有正确日期格式的字符串。

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

function mergeData(a,b){
    return _.mergeWith(a,b, (a,b)=>{
        if(typeof(a) == "string") return new Date(a);
        if(typeof(a) == "number") return a+b;
    });
}
console.log(mergeData(object1, object2));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>

答案 2 :(得分:0)

从可读性的角度来看,将其分解为较小的问题可能更容易。例如,您需要能够在日期上合并子数组,还需要为每个对象中的不同键集做好准备。

这解决了每个问题:

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

// a function to merge arrays with defaulf values
// in  case one is not defined
function mergeArray(a1 =[], a2 =[]){
    let hash  = [...a1, ...a2].reduce((hash, {timestamp, count}) =>{
        if(!hash.hasOwnProperty(timestamp)) hash[timestamp] = {timestamp, count:0}
        hash[timestamp].count += count
        return hash
    }, {})
    return Object.values(hash)
}

// a set of all keys between the tow objects
// so the objects don't nees to have the same keys
// we'll loop over this to make sure we get everything
const allKeys = new Set([...Object.keys(object1), ...Object.keys(object2)])

// now for each known key merge the arrays
let res = {}
for (key of allKeys){
    res[key] = mergeArray(object1[key], object2[key])
}
console.log(res)

答案 3 :(得分:0)

Lodash的_.mergeWith()回调将键作为第三个参数。您可以使用它来决定如何合并项目:

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/lodash@4.17.11/lodash.min.js"></script>