Объединить объекты с отсутствующим ключом - PullRequest
0 голосов
/ 11 декабря 2018

У меня есть два таких объекта:

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 и object2 могут иметь массив длины> 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}]}

(если object1 и object2 имеютмассив длины> 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}]}

)

Я использовал mergeWith Lodash таким образом:

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. , если object1 и object2 имеют одинаковый ключ, но еслиэто:

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

(объект 2 отсутствует 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 голосов
/ 11 декабря 2018

Обратный вызов Лодаша _.mergeWith() получает ключи как 3-й параметр.Вы можете использовать его, чтобы решить, как объединить элементы:

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>
0 голосов
/ 11 декабря 2018

Итак, что делает 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>
0 голосов
/ 11 декабря 2018

С точки зрения читабельности, вероятно, проще разбить это на более мелкие проблемы.Например, у вас должна быть возможность объединить подмассив по дате, и вам также нужно быть готовым к различным наборам ключей в каждом объекте.

Это касается каждого из них:

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)
0 голосов
/ 11 декабря 2018

Вы можете получить это через Array.reduce, Object.entries и Array.forEach in ES6 :

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, а затем выполнить 2-й forEach для суммирования значений.

Предполагается, что object1 и object2 всегда имеют одинаковую длину массивов .

...