【问题标题】:How to group array of dates by start time and end time and subtract last value - first value?如何按开始时间和结束时间对日期数组进行分组并减去最后一个值 - 第一个值?
【发布时间】:2021-12-14 01:26:21
【问题描述】:

我有按天分组的日期数组,我想按时间开始和结束对日期进行分组,然后减去最后一个值 - 第一个值。

时间开始和结束也在一个数组中。

数据数组:

const data = [
  { x: "2021-10-17T24:00:00.000Z", y: 52 },
  { x: "2021-10-17T22:14:00.000Z", y: 44 },
  { x: "2021-10-17T22:10:00.000Z", y: 36 },
  { x: "2021-10-17T22:00:00.000Z", y: 29 },
  { x: "2021-10-17T20:14:45.540Z", y: 24 },
  { x: "2021-10-17T20:13:45.540Z", y: 20 },
  { x: "2021-10-17T20:00:45.540Z", y: 18 },
  { x: "2021-10-17T14:38:45.540Z", y: 17 },
  { x: "2021-10-17T13:38:45.540Z", y: 15 },
  { x: "2021-09-16T14:36:46.540Z", y: 13 },
  { x: "2021-01-04T14:35:46.540Z", y: 12 },
  { x: "2021-01-01T14:30:46.540Z", y: 10 },
  { x: "2020-02-01T06:28:47.520Z", y: 7 },
  { x: "2020-02-01T07:28:47.520Z", y: 6 },
  { x: "2019-04-13T10:19:20.034Z", y: 5 },
  { x: "2018-01-01T09:09:19.134Z", y: 4 },
  { x: "2017-01-01T12:09:19.034Z", y: 3 },
  { x: "2016-01-02T12:10:20.034Z", y: 2 },
  { x: "2016-01-02T11:10:20.034Z", y: 1 }
];

时间开始和结束数组:

const configurations = [
  {
    label: "C1",
    startTime: { hr: "06", min: "00" },
    endTime: { hr: "22", min: "00" }
  },
  {
    label: "C2",
    startTime: { hr: "22", min: "00" },
    endTime: { hr: "24", min: "00" }
  }
];

按天分组的数据:

[
  {
    "value": 37, // 52 -15 (last value - first value)
    "label": "2021/10/17",
    // (I want to group this data by time start and end)
    "data": [
      {
        "x": "2021-10-17T13:38:45.540Z",
        "y": 15
      },
      {
        "x": "2021-10-17T14:38:45.540Z",
        "y": 17
      },
      {
        "x": "2021-10-17T20:00:45.540Z",
        "y": 18
      },
      {
        "x": "2021-10-17T20:13:45.540Z",
        "y": 20
      },
      {
        "x": "2021-10-17T20:14:45.540Z",
        "y": 24
      },
      {
        "x": "2021-10-17T22:00:00.000Z",
        "y": 29
      },
      {
        "x": "2021-10-17T22:10:00.000Z",
        "y": 36
      },
      {
        "x": "2021-10-17T22:14:00.000Z",
        "y": 44
      },
      {
        "x": "2021-10-17T24:00:00.000Z",
        "y": 52
      }
    ]
  },
//...
]

预期输出:

[
  {
    "value": 37, // 52 -15 (last value - first value)
    "label": "2021/10/17",
    "data": [
              {
                value: 14, // 29-15 =14
                label: "C1"
              },
              {
                value: 23, // 52-29 = 23
                label: "C2"
              }
            ]
  },
 // ...
]

这是我尝试过的Group data by time start and end

【问题讨论】:

  • 我想知道,22:00 作为 end 不包含在 C1 中(24 而不是 29 - 15),但 24:00 包含在 C2 (52-29) 中 - 这是正确的吗?跨度>
  • @Corrl 是的,我错了,它应该包含在 C1 和 C2 中。 (29-15 =14 不是 9)

标签: javascript arrays date array-reduce


【解决方案1】:

我没有遵循你所有脚本的逻辑。我将在下面评论:

parseInt(item.startTime.hr, 10) * 60

parseInt 是多余的,因为乘法会将字符串强制转换为数字。

我已将输入数据修改为使用 Date 对象而不是时间戳,并且所有计算都使用 UTC。

以下首先生成日期分组数据,然后处理配置数据。如果组中只有一天,则值为 0,因为它是第一个也是最后一个条目。如果没有符合配置类别的日期,则该值为空,因此不会添加到结果数据中。

其中一个forEach循环可以转换为reduce,但有时forEach在逻辑上更容易理解。 :-)

希望cmets足够了,请询问您是否需要澄清。

let data = [
  { x: new Date('2021-10-17T23:59:59.999Z'), y: 52 },
  { x: new Date('2021-10-17T22:14:00.000Z'), y: 44 },
  { x: new Date('2021-10-17T22:10:00.000Z'), y: 36 },
  { x: new Date('2021-10-17T22:00:00.000Z'), y: 29 },
  { x: new Date('2021-10-17T20:14:45.540Z'), y: 24 },
  { x: new Date('2021-10-17T20:13:45.540Z'), y: 20 },
  { x: new Date('2021-10-17T20:00:45.540Z'), y: 18 },
  { x: new Date('2021-10-17T14:38:45.540Z'), y: 17 },
  { x: new Date('2021-10-17T13:38:45.540Z'), y: 15 },
  { x: new Date('2021-09-16T14:36:46.540Z'), y: 13 },
  { x: new Date('2021-01-04T14:35:46.540Z'), y: 12 },
  { x: new Date('2021-01-01T14:30:46.540Z'), y: 10 },
  { x: new Date('2020-02-01T06:28:47.520Z'), y: 7 },
  { x: new Date('2020-02-01T07:28:47.520Z'), y: 6 },
  { x: new Date('2019-04-13T10:19:20.034Z'), y: 5 },
  { x: new Date('2018-01-01T09:09:19.134Z'), y: 4 },
  { x: new Date('2017-01-01T12:09:19.034Z'), y: 3 },
  { x: new Date('2016-01-02T12:10:20.034Z'), y: 2 },
  { x: new Date('2016-01-02T11:10:20.034Z'), y: 1 }
];

let configurations = [
  {
    label: "C1",
    startTime: { hr: "06", min: "00" },
    endTime: { hr: "22", min: "00" }
  },
  {
    label: "C2",
    startTime: { hr: "22", min: "00" },
    endTime: { hr: "24", min: "00" }
  }
];

function groupByDay(data) {
  // Used in reduce
  let currentDate, group;
  // Group by day
  let dayGroups = data.reduce((acc, item) => {
    // Get the date
    let date = item.x.toISOString().substring(0,10);
    // If a new date, start a new group
    if (date != currentDate) {
      currentDate = date;
      group = {value:'', label:date, data:[]};
      acc.push(group);
    }
    // Add item to start of data, update value
    group.data.unshift(item);
    group.value = group.data[group.data.length - 1].y - group.data[0].y;
    
    return acc;
  }, []);
  
  return dayGroups;
}

function processConfigs(dayGroups, config) {
  // Convert {hr, min} to ms
  let configTimeToMs = ({hr, min}) => hr*3.6e6 + min*6e4;
  // Get ms since start of UTC day
  let dateTimeToMs = (date) => date % 8.64e7;

  // For each dayGroup, collect data for each config
  return dayGroups.reduce((acc, dayGroup) => {
    // Create a new configGroup
    let configGroup = {value:dayGroup.value, label:dayGroup.label, data:[]};
    acc.push(configGroup);
    
    // For each config, add data to configGroup.data
    config.forEach(config => {
      let configStart = configTimeToMs(config.startTime);
      let configEnd = configTimeToMs(config.endTime);
      let configObj = {value: '', label: config.label};

      // Add data for each day
      let groupFirstValue = null;
      dayGroup.data.forEach(day => {
        let dayMs = dateTimeToMs(day.x);
        // If falls in config time range
        if (dayMs >=configStart && dayMs <= configEnd) {
          // If groupFirstValue not already set
          if (!groupFirstValue) {
            groupFirstValue = day.y;
          }
          // Update value
          configObj.value = day.y - groupFirstValue;
        }
      });
      // If value is empty, there are no days for this config so don't add
      if (configObj.value !== '') {
        configGroup.data.push(configObj);
      }
    });
    return acc;
  }, []);
}

let dayGroups = groupByDay(data);
// console.log(dayGroups);
console.log(processConfigs(dayGroups, configurations));

【讨论】:

  • 我尝试使用数据库中的某个日期,但出现此错误date.substring is not a function
  • 在 OP 中,data.x 是一个字符串。如果它实际上是一个 Date 对象,那么你需要这么说。像“2021-10-17T24:00:00.000Z”而不是“2021-10-18T00:00:00.000Z”这样的时间戳是非常不寻常的。这将影响处理结果,因为后者不会包含在 2021-10-17 的数据组中,但前者会。
  • 也许这是正确的“2021-10-18T00:00:00.000Z”,因为我对同一天或第二天感到困惑。但在数据库中我认为没有 24:00:00 格式。
  • 我修复了错误 date.substring is not a function: with this line const [year, month, day]=new Date(iso_string).toISOString('yyyy-MM-ddTHH:mm:ss.sssZ') .split(/\D/g) but I still have problem with "value": "", It is still empty string.
  • @DevM——似乎 iso_string 实际上是一个日期。如果是这样,请将示例数据更改为 new Date( x: '2021-10-17T22:14:00.000Z'),以便日期是日期,而不是字符串。 toISOString 不接受任何参数,因此它们被忽略。我怀疑 24:00:00 的时间是生成样本数据的错误。 split 默认情况下会在每个匹配项上拆分,因此 g 标志是多余的。无论如何,我会相应地更新上述内容。
猜你喜欢
  • 2021-12-11
  • 2021-01-19
  • 1970-01-01
  • 2016-09-07
  • 2023-01-15
  • 1970-01-01
  • 2012-04-20
  • 2012-10-25
  • 1970-01-01
相关资源
最近更新 更多