用Lodash重复日期展平对象数组并获得唯一的键和值吗?

时间:2019-06-13 21:46:56

标签: javascript arrays lodash

我一直在和Lodash玩耍,并且没有接近一个不涉及很多额外循环和开销的解决方案。

data: [
    {
      name: "FirstResult", values: [
        {
          value: { NameCount: 1, OtherCount: 1 },
          date: 2019-05-15T07:00:00+0000
        },
        {
          value: { NameCount: 1 },
          date: 2019-05-16T07:00:00+0000
        }
      ]
    },
    {
      name: "SecondResult",
      values: [
        {
          value: { NameCount: 1 },
          date: 2019-05-15T07:00:00+0000
        },
        {
          value: { BuyCount: 2, SellCount: 1 },
          date: 2019-05-16T07:00:00+0000
        }
      ]
    }
  ]

我想弄平它,并通过使用日期作为键来返回和返回一些配置,以使其合并和聚合:

[ 
  { date: 2019-05-15T07:00:00+0000, values: { NameCount: 2, OtherCount: 1 } },
  { date: 2019-05-16T07:00:00+0000, values: { NameCount: 1, BuyCount: 2, SellCount: 1 } }
]

或者甚至只是一个平面对象数组都可以,例如:

[ 
  { date: 2019-05-15T07:00:00+0000, NameCount: 2, OtherCount: 1 },
  { date: 2019-05-16T07:00:00+0000, NameCount: 1, BuyCount: 2, SellCount: 1 }
]

有人对用Lodash或Vanilla解决方案实现此方法有任何想法吗?

2 个答案:

答案 0 :(得分:2)

您可以使用lodash链将其展平,按日期分组,然后将每个分组映射并合并到单个对象:

const fn = data => _(data)
  .flatMap('values') // flatten to array of objects
  .groupBy(o => o.date.toISOString()) // group by the iso representation 
  .map(group => { // map the groups by merging, and converting to require format
    const { date, value } = _.mergeWith({}, ...group, (objValue, srcValue) =>
      _.isNumber(objValue) ? objValue + srcValue : undefined // combine numeric values
    )
    
    return {
      date,
      ...value,
    }
  })
  .value()

const data = [{"name":"FirstResult","values":[{"value":{"NameCount":1,"OtherCount":1},"date": new Date("2019-05-15T07:00:00.000Z")},{"value":{"NameCount":1},"date": new Date("2019-05-16T07:00:00.000Z")}]},{"name":"SecondResult","values":[{"value":{"NameCount":1},"date":new Date("2019-05-15T07:00:00.000Z")},{"value":{"BuyCount":2,"SellCount":1},"date": new Date("2019-05-16T07:00:00.000Z")}]}]

const result = fn(data)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

或者您可以使用_.flow()生成函数(I'm using lodash/fp here):

const { flow, flatMap, groupBy, map, mergeAllWith, cond, isNumber, add } = _

const fn = flow(
  flatMap('values'), // flatten to array of objects
  groupBy(o => o.date.toISOString()), // group by the iso representation
  map(mergeAllWith(cond([[isNumber, add]]))), // combine numeric values
  map(({ date, value }) => ({ date, ...value })) // format the objects
)

const data = [{"name":"FirstResult","values":[{"value":{"NameCount":1,"OtherCount":1},"date": new Date("2019-05-15T07:00:00.000Z")},{"value":{"NameCount":1},"date": new Date("2019-05-16T07:00:00.000Z")}]},{"name":"SecondResult","values":[{"value":{"NameCount":1},"date":new Date("2019-05-15T07:00:00.000Z")},{"value":{"BuyCount":2,"SellCount":1},"date": new Date("2019-05-16T07:00:00.000Z")}]}]

const result = fn(data)

console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>

答案 1 :(得分:0)

这是基于Array.reduceArray.forEach作为对象键的纯 ES6 解决方案:

const data = [{"name":"FirstResult","values":[{"value":{"NameCount":1,"OtherCount":1},"date": new Date("2019-05-15T07:00:00.000Z")},{"value":{"NameCount":1},"date": new Date("2019-05-16T07:00:00.000Z")}]},{"name":"SecondResult","values":[{"value":{"NameCount":1},"date":new Date("2019-05-15T07:00:00.000Z")},{"value":{"BuyCount":2,"SellCount":1},"date": new Date("2019-05-16T07:00:00.000Z")}]}]

let result = data.reduce((r, { values }) => {
  values.forEach(({ value, date }) => {
    let keys = Object.keys(value), d = date.toISOString()
    r[d] = r[d] || Object.assign({}, ...keys.map(x => ({ date: d, [x]: 0 })))
    keys.forEach(k => r[d][k] = (r[d][k] || 0) + value[k])
  })
  return r
}, {})

console.log(Object.values(result))

主要思想是对值对象的键进行白色迭代,并在按日期分组时用它们组成对象。然后最后forEach只对每个结果对象值求和。