Transform JSON by grouping by date and thereafter by datetime in NodeJS

半世苍凉 提交于 2020-07-22 06:06:11

问题


I have a JSON of IoT readings as follow:

[
    {
        "datetime": "2020-06-29T23:59:59.000Z",
        "Benzene": 1.9
    },
    {
        "datetime": "2020-06-30T00:59:59.000Z",
        "Benzene": 0.6
    },
    {
        "datetime": "2020-06-30T01:59:59.000Z",
        "Benzene": 5.7
    },
    {
        "datetime": "2020-06-30T02:59:59.000Z",
        "Benzene": 5.5
    },
    {
        "datetime": "2020-06-30T03:59:59.000Z",
        "Benzene": 5.2
    },
    {
        "datetime": "2020-06-30T04:59:59.000Z",
        "Benzene": 1
    },
    {
        "datetime": "2020-06-30T05:59:59.000Z",
        "Benzene": 2.1
    },
    {
        "datetime": "2020-06-30T06:59:59.000Z",
        "Benzene": 4.2
    },
    {
        "datetime": "2020-06-30T07:59:59.000Z",
        "Benzene": 3.4
    },
    {
        "datetime": "2020-06-30T00:59:59.000Z",
        "NO2": 16.3
    },
    {
        "datetime": "2020-06-30T01:59:59.000Z",
        "NO2": 12.6
    },
    {
        "datetime": "2020-06-30T02:59:59.000Z",
        "NO2": 11.7
    },
    {
        "datetime": "2020-06-30T03:59:59.000Z",
        "NO2": 35.5
    },
    {
        "datetime": "2020-06-30T04:59:59.000Z",
        "NO2": 44.6
    },
    {
        "datetime": "2020-06-30T05:59:59.000Z",
        "NO2": 19.9
    },
    {
        "datetime": "2020-06-30T06:59:59.000Z",
        "NO2": 11.2
    },
    {
        "datetime": "2020-06-30T07:59:59.000Z",
        "NO2": 8.2
    },
    {
        "datetime": "2020-06-29T23:59:59.000Z",
        "O3": 25.6
    },
    {
        "datetime": "2020-06-30T01:59:59.000Z",
        "O3": 31.7
    },
    {
        "datetime": "2020-06-30T02:59:59.000Z",
        "O3": 35.1
    },
    {
        "datetime": "2020-06-30T03:59:59.000Z",
        "O3": 11.4
    },
    {
        "datetime": "2020-06-30T04:59:59.000Z",
        "O3": 4.9
    },
    {
        "datetime": "2020-06-30T05:59:59.000Z",
        "O3": 32.8
    },
    {
        "datetime": "2020-06-30T06:59:59.000Z",
        "O3": 46.4
    },
    {
        "datetime": "2020-06-30T07:59:59.000Z",
        "O3": 55.6
    }
]

I want to transform the JSON schema by grouping data by date only and inside it shows, into a single object, all readings (Benzene, O3, NO2) for the same datetime as follow:

[
  {
    "date": "2020-06-29",
    "values": [
      {
        "Benzene": 1.9,
        "t": "2020-06-29T23:59:59.000Z",
        "O3": 25.6
      }
    ]
  },
  {
    "date": "2020-06-30",
    "values": [
      {
        "Benzene": 0.6,
        "t": "2020-06-30T00:59:59.000Z",
        "NO2": 16.3
      },
      {
        "Benzene": 5.7,
        "t": "2020-06-30T01:59:59.000Z",
        "NO2": 12.6,
        "O3": 31.7
      },
      {
        "Benzene": 5.5,
        "t": "2020-06-30T02:59:59.000Z",
        "NO2": 11.7,
        "O3": 35.1
      },
      {
        "Benzene": 5.2,
        "t": "2020-06-30T03:59:59.000Z",
        "NO2": 35.5,
        "O3": 11.4
      },
      {
        "Benzene": 1,
        "t": "2020-06-30T04:59:59.000Z",
        "NO2": 44.6,
        "O3": 4.9
      },
      {
        "Benzene": 2.1,
        "t": "2020-06-30T05:59:59.000Z",
        "NO2": 19.9,
        "O3": 32.8
      },
      {
        "Benzene": 4.2,
        "t": "2020-06-30T06:59:59.000Z",
        "NO2": 11.2,
        "O3": 46.4
      },
      {
        "Benzene": 3.4,
        "t": "2020-06-30T07:59:59.000Z",
        "NO2": 8.2,
        "O3": 55.6
      }
    ]
  }
]

I used the following approach probably quite redundant and not particularly efficient. (json_from_csv_filtered contains the original JSON reported at the top)

   const data = Object.values(json_from_csv_filtered.reduce((groups, {
        datetime,
        ...rest
    }) => {
        if (!groups[datetime]) {
            groups[datetime] = {}
        }
        key = Object.keys({
            ...rest
        })
        value = Object.values({
            ...rest
        })
        groups[datetime][key[0]] = value[0];
        groups[datetime]['t'] = datetime
        return groups;
    }, []));

    const result = data.reduce((groups, readings) => {
        const date = readings.t.split('T')[0];
        if (!groups[date]) {
            groups[date] = [];
        }
        groups[date].push(readings);
        return groups;
    }, {})

    const groupArrays = Object.keys(result).map((date) => {
        return {
            date,
            values: result[date]
        };
    });


    console.log(JSON.stringify(groupArrays, null, 2));

Could anyone suggest a better approach? Moreover, 't' key, representing readings' datetime, I don't know why, appear always after 'Benzene' key, I would like it should be the opposite.

Thanks.


回答1:


You could take two hash table for long and short date and get the short date's values.

For getting a deeper understanding, you could have a look to the r object which contains two properties long and short.

{
    long: {
        "2020-06-29T23:59:59.000Z": {              // id: 3
            t: "2020-06-29T23:59:59.000Z",
            Benzene: 1.9,
            O3: 25.6
        },
        "2020-06-30T00:59:59.000Z": {              // id: 4
            t: "2020-06-30T00:59:59.000Z",
            Benzene: 0.6,
            NO2: 16.3
        },
        "2020-06-30T01:59:59.000Z": {              // id: 5
            t: "2020-06-30T01:59:59.000Z",
            Benzene: 5.7,
            NO2: 12.6,
            O3: 31.7
        },
        // ...
    },
    short: {
        "2020-06-29": {
            date: "2020-06-29",
            values: [
                {                                  // ref: 3,
                    t: "2020-06-29T23:59:59.000Z",
                    Benzene: 1.9,
                    O3: 25.6
                }
            ]
        },
        "2020-06-30": {
            date: "2020-06-30",
            values: [
                {                                  // ref: 4,
                    t: "2020-06-30T00:59:59.000Z",
                    Benzene: 0.6,
                    NO2: 16.3
                },
                {                                  // ref: 5,
                    t: "2020-06-30T01:59:59.000Z",
                    Benzene: 5.7,
                    NO2: 12.6,
                    O3: 31.7
                }
            ]
        },
        // ...
    }
}

long contains objects for collecting the properties for a lonmg timestamp and pushes this object to the corresponding short collection which is grouped by the short date of a day.

In the short property, all objects are finaly part of the result set, with grouped day data and grouped by timestamp, the long date.

var data = [{ datetime: "2020-06-29T23:59:59.000Z", Benzene: 1.9 }, { datetime: "2020-06-30T00:59:59.000Z", Benzene: 0.6 }, { datetime: "2020-06-30T01:59:59.000Z", Benzene: 5.7 }, { datetime: "2020-06-30T02:59:59.000Z", Benzene: 5.5 }, { datetime: "2020-06-30T03:59:59.000Z", Benzene: 5.2 }, { datetime: "2020-06-30T04:59:59.000Z", Benzene: 1 }, { datetime: "2020-06-30T05:59:59.000Z", Benzene: 2.1 }, { datetime: "2020-06-30T06:59:59.000Z", Benzene: 4.2 }, { datetime: "2020-06-30T07:59:59.000Z", Benzene: 3.4 }, { datetime: "2020-06-30T00:59:59.000Z", NO2: 16.3 }, { datetime: "2020-06-30T01:59:59.000Z", NO2: 12.6 }, { datetime: "2020-06-30T02:59:59.000Z", NO2: 11.7 }, { datetime: "2020-06-30T03:59:59.000Z", NO2: 35.5 }, { datetime: "2020-06-30T04:59:59.000Z", NO2: 44.6 }, { datetime: "2020-06-30T05:59:59.000Z", NO2: 19.9 }, { datetime: "2020-06-30T06:59:59.000Z", NO2: 11.2 }, { datetime: "2020-06-30T07:59:59.000Z", NO2: 8.2 }, { datetime: "2020-06-29T23:59:59.000Z", O3: 25.6 }, { datetime: "2020-06-30T01:59:59.000Z", O3: 31.7 }, { datetime: "2020-06-30T02:59:59.000Z", O3: 35.1 }, { datetime: "2020-06-30T03:59:59.000Z", O3: 11.4 }, { datetime: "2020-06-30T04:59:59.000Z", O3: 4.9 }, { datetime: "2020-06-30T05:59:59.000Z", O3: 32.8 }, { datetime: "2020-06-30T06:59:59.000Z", O3: 46.4 }, { datetime: "2020-06-30T07:59:59.000Z", O3: 55.6 }],
    result = Object.values(data
        .reduce((r, { datetime: t, ...o }) => {
            let date = t.slice(0, 10);
            if (!r.long[t]) {
                r.short[date] = r.short[date] || { date, values: [] };
                r.short[date].values.push(r.long[t] = { t });
            }
            Object.assign(r.long[t], o);
            return r;
        }, { long: {}, short: {} })
        .short
    );

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



回答2:


I would suggest using momentjs(https://momentjs.com/). It's a library that contains many utils for dealing with dates and times, I strongly recommend it!

Here is an example:

const exampleData = [{
    "datetime": "2020-06-29T23:59:59.000Z",
    "Benzene": 1.9
  },
  {
    "datetime": "2020-06-30T00:59:59.000Z",
    "Benzene": 0.6
  },
  {
    "datetime": "2020-06-30T01:59:59.000Z",
    "Benzene": 5.7
  },
  {
    "datetime": "2020-06-30T02:59:59.000Z",
    "Benzene": 5.5
  },
  {
    "datetime": "2020-06-30T03:59:59.000Z",
    "Benzene": 5.2
  },
  {
    "datetime": "2020-06-30T04:59:59.000Z",
    "Benzene": 1
  },
  {
    "datetime": "2020-06-30T05:59:59.000Z",
    "Benzene": 2.1
  },
  {
    "datetime": "2020-06-30T06:59:59.000Z",
    "Benzene": 4.2
  },
  {
    "datetime": "2020-06-30T07:59:59.000Z",
    "Benzene": 3.4
  },
  {
    "datetime": "2020-06-30T00:59:59.000Z",
    "NO2": 16.3
  },
  {
    "datetime": "2020-06-30T01:59:59.000Z",
    "NO2": 12.6
  },
  {
    "datetime": "2020-06-30T02:59:59.000Z",
    "NO2": 11.7
  },
  {
    "datetime": "2020-06-30T03:59:59.000Z",
    "NO2": 35.5
  },
  {
    "datetime": "2020-06-30T04:59:59.000Z",
    "NO2": 44.6
  },
  {
    "datetime": "2020-06-30T05:59:59.000Z",
    "NO2": 19.9
  },
  {
    "datetime": "2020-06-30T06:59:59.000Z",
    "NO2": 11.2
  },
  {
    "datetime": "2020-06-30T07:59:59.000Z",
    "NO2": 8.2
  },
  {
    "datetime": "2020-06-29T23:59:59.000Z",
    "O3": 25.6
  },
  {
    "datetime": "2020-06-30T01:59:59.000Z",
    "O3": 31.7
  },
  {
    "datetime": "2020-06-30T02:59:59.000Z",
    "O3": 35.1
  },
  {
    "datetime": "2020-06-30T03:59:59.000Z",
    "O3": 11.4
  },
  {
    "datetime": "2020-06-30T04:59:59.000Z",
    "O3": 4.9
  },
  {
    "datetime": "2020-06-30T05:59:59.000Z",
    "O3": 32.8
  },
  {
    "datetime": "2020-06-30T06:59:59.000Z",
    "O3": 46.4
  },
  {
    "datetime": "2020-06-30T07:59:59.000Z",
    "O3": 55.6
  },
  {
    "datetime": "2020-06-20T07:59:59.000Z",
    "O3": 14.6
  },
  {
    "datetime": "2020-06-20T07:59:59.000Z",
    "Benzene": 14.6
  }
]

const keyFormat = "YYYY-MM-DD"
const groupReadings = (readings) => {
  const groups = []
  readings.forEach(reading => {
    const dateMoment = moment(reading.datetime)
    const dateKey = dateMoment.format(keyFormat)

    let dateData = groups.find(x => x.date === dateKey)
    if (!dateData) {
      dateData = {
        date: dateKey,
        values: []
      }
      groups.push(dateData)
    }

    let groupedReading = dateData.values.find(x => x.t === reading.datetime)
    if (!groupedReading) {
      groupedReading = {
        t: reading.datetime
      }
      dateData.values.push(groupedReading)
    }

    const key = Object.keys(reading).filter(x => x !== 'datetime')[0]
    groupedReading[key] = reading[key]
  })
  
  return groups
}

const result = groupReadings(exampleData)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js"></script>

By using MomentJs your code will be much more readable and more easily maintained. I hope that helps, good luck!

Edit: Fixed example



来源:https://stackoverflow.com/questions/62654005/transform-json-by-grouping-by-date-and-thereafter-by-datetime-in-nodejs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!