使用lodash groupby的每月水平总和?

Monthly sum of level using lodash groupby?

我想使用 lodash groupby 对对象数组进行一些转换。首先应用到 2 个键 clazz_id,然后应用到月份,得到级别的总和。

import './App.css'
import _ from 'lodash'
import { useEffect } from 'react'
import moment from 'moment'
import groupBy from 'lodash/groupBy'

const data1 = [
  {
    id: 1,
    clazz_id: 1,
    answers: [
      { level: 8, created_at: '2022-01-19T09:13:42.149+08:00' },
      { level: 1, created_at: '2022-02-19T09:13:42.149+08:00' },
    ],
  },
  {
    id: 2,
    clazz_id: 2,
    answers: [
      { level: 7, created_at: '2022-01-19T09:13:42.149+08:00' },
      { level: 2, created_at: '2022-03-19T09:13:42.149+08:00' },
    ],
  },
  {
    id: 3,
    clazz_id: 2,
    answers: [
      { level: 3, created_at: '2022-01-19T09:13:42.149+08:00' },
      { level: 8, created_at: '2022-02-19T09:13:42.149+08:00' },
    ],
  },
]

const App = () => {
  const data2 = []

  useEffect(() => {
    data1.map((item) => {
      item.answers.map((e) => {
        data2.push({
          clazz_id: item.clazz_id,
          level: e.level,
          month: moment(e.created_at).format('MMM'),
        })
      })
    })
    const a = groupBy(data2, 'clazz_id', 'month')
    console.log(a)
  }, [])

  return <div className='App'>test</div>
}

export default App

但是,月份应用不正确。任何帮助将不胜感激。

结果应该是:

[{clazz_id: 1, level: 8, month: 'Jan'},
{clazz_id: 1, level: 1, month: 'Feb'}]


[{clazz_id: 2, level: 10, month: 'Jan'},
{clazz_id: 2, level: 8, month: 'Feb'},
{clazz_id: 2, level: 2, month: 'Mar'}]

您可以通过多次调用 Array.reduce()、根据 clazz_idmonth.

创建分组键来获得所需的结果

然后我们将共享相同键的任何项目的 level 值相加:

const data1 = [ { id: 1, clazz_id: 1, answers: [ { level: 8, created_at: '2022-01-19T09:13:42.149+08:00' }, { level: 1, created_at: '2022-02-19T09:13:42.149+08:00' }, ], }, { id: 2, clazz_id: 2, answers: [ { level: 7, created_at: '2022-01-19T09:13:42.149+08:00' }, { level: 2, created_at: '2022-03-19T09:13:42.149+08:00' }, ], }, { id: 3, clazz_id: 2, answers: [ { level: 3, created_at: '2022-01-19T09:13:42.149+08:00' }, { level: 8, created_at: '2022-02-19T09:13:42.149+08:00' }, ], }, ]

const result = Object.values(data1.reduce((acc, { clazz_id, answers }) => { 
    return answers.reduce((acc, { level, created_at }) => {
        const month = moment(created_at).format('MMM');
        const key = `${clazz_id}-${month}`;
        acc[key] = acc[key] || { clazz_id, level: 0, month };
        acc[key].level += level
        return acc;
    }, acc);
}, {}));

console.log('Result:')
result.forEach(r => console.log(JSON.stringify(r)))
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

对于普通 Javascript,您可以使用嵌套对象进行缩减。

const
    data = [{ id: 1, clazz_id: 1, answers: [{ level: 8, created_at: '2022-01-19T09:13:42.149+08:00' }, { level: 1, created_at: '2022-02-19T09:13:42.149+08:00' }] }, { id: 2, clazz_id: 2, answers: [{ level: 7, created_at: '2022-01-19T09:13:42.149+08:00' }, { level: 2, created_at: '2022-03-19T09:13:42.149+08:00' }] }, { id: 3, clazz_id: 2, answers: [{ level: 3, created_at: '2022-01-19T09:13:42.149+08:00' }, { level: 8, created_at: '2022-02-19T09:13:42.149+08:00' }] }],
    result = Object
        .values(data.reduce((r, { clazz_id, answers }) => {
            r[clazz_id] ??= {};
            answers.forEach(({ level, created_at }) => {
                const 
                    months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
                    monthV = new Date(created_at).getMonth(),
                    month = months[monthV];

                r[clazz_id][monthV] ??= { clazz_id, level: 0, month };
                r[clazz_id][monthV].level += level;
            });
            return r;
        }, {}))
        .map(Object.values);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

这可能也行。数据按一定方式排列后应用reduce。

import './App.css'
import _ from 'lodash'
import { useEffect } from 'react'
import moment from 'moment'
import groupBy from 'lodash/groupBy'

const data1 = [
  {
    id: 1,
    clazz_id: 1,
    answers: [
      { level: 8, created_at: '2022-01-19T09:13:42.149+08:00' },
      { level: 1, created_at: '2022-02-19T09:13:42.149+08:00' },
    ],
  },
  {
    id: 2,
    clazz_id: 2,
    answers: [
      { level: 7, created_at: '2022-01-19T09:13:42.149+08:00' },
      { level: 2, created_at: '2022-03-19T09:13:42.149+08:00' },
    ],
  },
  {
    id: 3,
    clazz_id: 2,
    answers: [
      { level: 3, created_at: '2022-01-19T09:13:42.149+08:00' },
      { level: 8, created_at: '2022-02-19T09:13:42.149+08:00' },
    ],
  },
]

const App = () => {
  const data2 = []

  useEffect(() => {
    data1.map((item) => {
      item.answers.map((e) => {
        data2.push({
          clazz_id: item.clazz_id,
          level: e.level,
          month: moment(e.created_at).format('MMM'),
        })
      })
    })

    console.log(data2)

    var o = {}
    var result = data2.reduce(function (r, e) {
      var key = e.clazz_id + '|' + e.month
      if (!o[key]) {
        o[key] = e
        r.push(o[key])
      } else {
        o[key].level += e.level
      }
      return r
    }, [])

    console.log(result)
  }, [])

  return <div className='App'>test</div>
}

export default App