【问题标题】:How to filter last day in an array?如何过滤数组中的最后一天?
【发布时间】:2020-03-04 05:53:19
【问题描述】:

我有一个这样的对象数组:

[
    {
        created: "2019-08-14T13:24:36Z",
        email: "test1@gmail.com"
    },
    {
        created: "2019-08-15T13:24:36Z",
        email: "test2@gmail.com"
    },
    {
        created: "2019-08-16T13:24:36Z",
        email: "test1@gmail.com"
    },
    {
        created: "2019-08-22T13:24:36Z",
        email: "test4@gmail.com"
    },
    {
        created: "2019-08-22T15:29:66Z",
        email: "test1@gmail.com"
    }
]

数组按created 排序。我想filter 那些在最后一天的记录,不管那天的时间。我使用moment.js 添加了时间戳。这些方面的内容:

router.get('/GetLastDayRecords', (req, res) => {
    res.json(allRecords.filter(record => record.created.max()));
});

【问题讨论】:

  • 到目前为止,您有什么尝试自己解决的?
  • @Andreas 我尝试做this,但无法正确解决。
  • 数组已经排序。只需抓住最后一个元素并遍历数组以找到应该保留/删除的元素 (Array.prototype.filter())。

标签: javascript node.js typescript date momentjs


【解决方案1】:

拆分任务:首先获取排序数组末尾的最大日期(只需获取其中的“YYYY-MM-DD”部分就足够了),然后启动过滤器:

let max = allRecords.length ? allRecords[allRecords.length-1].created.slice(0,10) : "";
res.json(allRecords.filter(({created}) => created >= max));

【讨论】:

  • 这是一个很好的答案!我不知道您可以使用普通的比较运算符 (created >= max) 来比较这样的日期字符串。我不得不自己仔细检查它并且它有效!很棒的东西。谢谢。
  • @selmanbey 这不是“神奇”的日期字符串比较。它只适用于 yyyy-mm-dd 格式。
【解决方案2】:

首先,您需要弄清楚哪一天最后一天。如果您可以假设记录已经排序,那么这很简单:

// Assuming your records are stored in the variable "records"

var lastDay = records[records.length - 1].created;

现在您的具体答案可能会有所不同,具体取决于您要如何处理时区。假设一个事件发生在美国东部标准时间晚上 11 点(格林威治标准时间上午 3 点),另一事件发生在美国东部标准时间上午 1 点(格林威治标准时间上午 5 点)。这些是同一天吗?在欧洲他们是,但在美国他们不是!

您需要做的是从列出的日期+时间到“日”创建一些密码。这样您就可以比较两个“天”,看看它们是否相同:

lastDay = new Date(lastDay);
// Setting hours, minutes, and seconds to 0 will give you just the "day" without the time, but by default will use the system timezone
lastDay.setHours(0);
lastDay.setMinutes(0);
lastDay.setSeconds(0);

一旦您知道最后一天是哪一天,它就是一个简单的过滤器:

// Using a for loop
var results = []
for (var i = 0; i < records.length; i++)
{
    if (records[i].created > lastDay) {
        results.push(records[i]);
    }
}

// Using .filter
var results = records.filter(x => x.created > lastDay);

或者,由于我们知道它已经排序,我们可以通过二进制搜索最后一天的第一条记录,然后抓取之后的所有记录来更有效地做到这一点:

var test = records.length / 2;
var step = records.length / 4;
var found = false;
while (!found) {
    if (records[test].created < lastDay) {
        test += step;
        step /= 2;
    }
    else if (records[test].created > lastDay) {
        if (step == 1) {
            // We found the exact cut-off
            found = true;
        }
        else {
            test -= step;
            step /= 2;
        }
    }
}

var results = records.slice(test);

因为您只对“最后”一天感兴趣,所以逻辑要简单一些。如果您想要“第三天”,您需要检查created 是否在第三天开始之后在第三天结束之前。我们可以检查它是否在最后一天开始之后。

【讨论】:

    【解决方案3】:

    这应该可行:

    allRecords.filter( record => {
        let last_date = allRecords[ allRecords.length - 1].created
        return last_date.slice(0, 10) === record.created.slice(0, 10)
    })
    

    基本上,您从数组中获取最后一个元素并将其created 值切分到其日期。然后,您将当前记录的 created 值切分到其日期并比较它们是否相同。

    【讨论】:

      【解决方案4】:

      我用reduce和filter写了一个解决方案:

      const lastDay = arr.reduce((acc, el) => {
          const date = el.created.substr(0,10);
          const oldDate = new Date(acc);
          const nextDate = new Date(date);
          if(oldDate.getTime() > nextDate.getTime()) {
              return oldDate;
          } else {
              return nextDate;
          }
      }, '1900-01-01');
      
      const lastDayArr = arr.filter(el => {
          const date = el.created.substr(0,10);
          const oldDate = new Date(lastDay);
          const nextDate = new Date(date);
          return (oldDate.getTime() === nextDate.getTime());
      });
      

      首先,您找到最近的日期,通过比较哪个日期是最近的来减少原始数组,为此您删除创建的字符串中指定小时/分钟/秒的部分 . 您可以使用非常遥远的时间日期作为初始值,也可以将其设置为 null 并在回调函数中添加另一个验证。

      作为第二步,您使用过滤器,使用删除创建字符串的 小时/分钟/秒 的相同技术。

      最终结果是原始数组中日期最近的元素组成的数组。

      如果您可以假设数组已排序,则可以跳过 reduce 方法并执行以下操作:

      const lastDay = arr[arr.length - 1].created.substr(0,10);
      

      【讨论】:

      • 这不是最简洁的解决方案,但我认为它对其他程序员来说是最容易阅读和推理的
      【解决方案5】:

      我会创建一个函数来将您创建的属性转换为易于比较的数据。

      我也会避免尝试在一两行中完成整个过滤器操作,因为其他开发人员很难阅读。

      const dateToInt = date => parseInt( date.split('T').shift().replace(/-/g, '') );
      

      以上将:

      • 将您创建的属性拆分为日期和时间数组。
      • 选择第一个元素,恰好是日期。
      • 删除日期中的破折号。
      • 将值强制转换为数字。

      有了这个,您可以找到最大值并根据该值进行过滤。


      const nums = foo.map( ({ created }) => dateToInt(created) )
      

      首先从数据集中得到一个数字列表。

      const max = Math.max( ...nums )
      

      获取列表中最大的数字。

      const lastDays = foo.filter( ({ created }) => dateToInt(created) === max )
      

      通过所有这些设置,获取最大日期非常简单易读。


      当然,因为列表已经排序。你也可以这样做。

      const last = foo[foo.length -1].created;
      
      const lastDays = foo.filter( ({ created }) => created === last )
      

      【讨论】:

        【解决方案6】:

        假设数组已经是ASC 有序:

        const onLastDay = values.filter( v => {
          const last = moment(values[ values.length - 1 ].created)
          const differenceInDays = last.diff(moment(v.created), 'days')
          return differenceInDays < 1
        })
        
        console.log(onLastDay)
        

        注意:如果您尝试使用报告的数组,您会收到错误,因为最后日期无效!还有 66 秒!

        【讨论】:

          猜你喜欢
          • 2018-03-14
          • 1970-01-01
          • 2023-01-19
          • 2016-07-18
          • 2023-03-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多