如何按天、周、月对具有时间戳属性的对象进行分组?

Posted

技术标签:

【中文标题】如何按天、周、月对具有时间戳属性的对象进行分组?【英文标题】:How to group objects with timestamps properties by day, week, month? 【发布时间】:2014-01-05 00:14:15 【问题描述】:

在 nodejs 应用程序中,我有一个事件对象数组,格式如下:

eventsArray = [ id: 1, date: 1387271989749 , id:2, date: 1387271989760, ... ]

eventsArray 具有可变长度的 n 个元素,假设我选择时间参考为巴黎时间,我希望能够按天、周或月对元素进行分组:

groupedByDay = 

            2012:  ... ,
            2013: 
              dayN  : [id: a0, date: b0, id: a1, date: b1, id: a2, date: b2 ],
              dayN+1: [id: a3, date: b3, id: a4, date: b4, id: a5, date: b5 ],
              ...
                   ,
            2014:  ... 

          

groupedByWeek = 
            2012:  ... 
            2013: 
              weekN: [id: a0, date: b0, id: a1, date: b1, id: a2, date: b2 ],
              weekN+1: [id: a3, date: b3],
              ....
                  ,
             2014:  ... 
                

groupedByMonth = 
             2012:  ... ,
             2013: 
               monthN: [ id: a0, date: b0 , id: a1, b1, id: a2, b2, id: a3, b3 ],
               monthN+1: [ id: a4, date: b4 , id: a5, b5, id: a6, b6],
               ...
                   ,
              2014:  ... 
                 

在处理 unix 时间戳方面几乎没有经验,我想知道如何做到这一点,或者是否有一个 npm 模块可以让这更容易。

【问题讨论】:

您是否尝试过使用Date 提取日期然后进行相应排序? 另一个选项是moment.js,这使它变得微不足道:moment(1387271989749).week() groupedbyday and rest has 2012,2013... 是否需要先按年份分组? @user568109: 不一定,可能只是第 N 天、第 N+1 天等,其中 N 是自 1970 年以来的天数 与@punund 相同,只是使用 Momentjs。这是一个改变生活的人。 【参考方案1】:

我会做这样的事情:

var item,
    i = 0,
    groups = ,
    year, day;
while (item = eventsArray[i++]) 
    item = new Date(item.date);
    year = item.getFullYear();
    day = item.getDate();
    groups[year] || (groups[year] = ); // exists OR create 
    groups[year][day] || (groups[year][day] = []);  // exists OR create []
    groups[year][day].push(item);

此版本仅按天对项目进行分组,但您可以轻松地在数周和数月内获得相同的结果,用适当的函数替换 item.getDate()

getWeek() : https://***.com/a/6117889/1636522. getMonth() : MDN doc(从零开始!)。

【讨论】:

【参考方案2】:

这是我的解决方案。请记住,日、周和月是相对于起源而言的,因为纪元:

let event = [ id: 1, date: 1387271989749 , id:2, date: 1387271989760 ];

function groupday(value, index, array)
   let byday=;
    let d = new Date(value['date']);
    d = Math.floor(d.getTime()/(1000*60*60*24));
    byday[d]=byday[d]||[];
    byday[d].push(value);
  return byday


function groupweek(value, index, array)
  let byweek=;
   let d = new Date(value['date']);
    d = Math.floor(d.getTime()/(1000*60*60*24*7));
    byweek[d]=byweek[d]||[];
    byweek[d].push(value);
  return byweek


function groupmonth(value, index, array)
   let bymonth=;
    d = new Date(value['date']);
    d = (d.getFullYear()-1970)*12 + d.getMonth();
    bymonth[d]=bymonth[d]||[];
    bymonth[d].push(value);
  return bymonth


console.log('grouped by day', event.map(groupday));
console.log('grouped by week',event.map(groupweek));
console.log('grouped by month',event.map(groupmonth));


注意:对象是分组的,但日、周和月的键是数字的。只有年份的键是人类可读的。

【讨论】:

我很困惑,value['date']d.getTime() 本质上不一样吗?也就是说,您可以简单地使用Math.floor(value.date / 1000 * 60 * 60 * 24) 请为 op 格式化您的代码,您在变量之前错过了 let/const。【参考方案3】:

这样的东西可能很接近您的需要。

javascript Date 有 getFullYeargetDategetMonth 函数 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date),您也可以查看这篇 SO 帖子 (Convert a Unix timestamp to time in JavaScript)。

这里(排列)的基本方法是按年、然后按日和月建立哈希。

此代码不做周。你是指一年中的哪一周还是哪一个月?我怀疑您可以编写自己的日期方法来获取该数字,然后按照下面的模式获取您想要的所有数据。您可以从 JS (getDay) 获取星期几。我不确定你想如何计算周,但这种方法可能会有所帮助。

我在浏览器中运行了这段代码(在初始化了一组虚拟事件数组之后),但我怀疑它可以很好地转换为节点。

如果您愿意的话,您应该为函数正确命名,并且可以将所有方法移动到对象原型中。

希望对你有帮助

var groupEvents = function(eventsArray) 

  this.dates = eventsArray;

  this.arranged = ;

  this.monthKey = function(month) 
    return 'month' + month;
  ;
  this.dayKey = function(month) 
    return 'day' + month;
  ;
  this.getYear = function(year) 
    this.arranged[year] = this.arranged[year] || 
    return this.arranged[year]
  ;
  this.getMonth = function(month, year) 
    var y = this.getYear(year);
    var k = this.monthKey(month);
    y[k] = y[k] || [];
    return y[k]
  ;
  this.getDate = function(day, year) 
    var y = this.getYear(year);
    var k = this.dayKey(day);
    y[k] = y[k] || [];
    return y[k]
  ;
  this.addToMonth = function(info, month, year) 
    var y = this.getMonth(month,year);
    y.push(info);
  ;
  this.addToDay = function(info, day, year) 
    var y = this.getDate(day,year);
    y.push(info);
  ;
  this.breakdownDate = function(date) 
    return 
      month: date.getMonth(),
      day: date.getDate(),
      year: date.getFullYear()
    ;
  
  /** grab a date, break it up into day, month year
      and then categorize it */
  this.arrange = function() 
    if(!this.arranged.length) 
      var ii = 0;
      var nn = this.dates.length;
      for(; ii < nn; ++ii ) 
        var el = this.dates[ii];
        var date = new Date(el.date * 1000);
        var parsed = this.breakdownDate(date);
        this.addToMonth(el, parsed.month, parsed.year);
        this.addToDay(el, parsed.month, parsed.year);
      
    
    return this.arranged;
  ;
  return this;
;

if(eventArray.length) 
  var events = new groupEvents(eventArray);
  var arranged = events.arrange();
  console.log(arranged);

【讨论】:

【参考方案4】:

扩展@user568109 的答案,这是正确且投票赞成的,这是一个可以完成所有工作的函数,为了演示,使用更大的数组:

// Group by time period - By 'day' | 'week' | 'month' | 'year'
// ------------------------------------------------------------
var groupByTimePeriod = function (obj, timestamp, period) 
  var objPeriod = ;
  var oneDay = 24 * 60 * 60 * 1000; // hours * minutes * seconds * milliseconds
  for (var i = 0; i < obj.length; i++) 
    var d = new Date(obj[i][timestamp] * 1000);
    if (period == 'day') 
      d = Math.floor(d.getTime() / oneDay);
     else if (period == 'week') 
      d = Math.floor(d.getTime() / (oneDay * 7));
     else if (period == 'month') 
      d = (d.getFullYear() - 1970) * 12 + d.getMonth();
     else if (period == 'year') 
      d = d.getFullYear();
     else 
      console.log('groupByTimePeriod: You have to set a period! day | week | month | year');
    
    // define object key
    objPeriod[d] = objPeriod[d] || [];
    objPeriod[d].push(obj[i]);
  
  return objPeriod;
;

var eventsArray = [ id: 1, date: 1317906596 ,  id: 2, date: 1317908605 ,  id: 3, date: 1317909229 ,  id: 4, date: 1317909478 ,  id: 5, date: 1317909832 ,  id: 6, date: 1317979141 ,  id: 7, date: 1317979232 ,  id: 8, date: 1317986965 ,  id: 9, date: 1318582119 ,  id: 10, date: 1318595862 ,  id: 11, date: 1318849982 ,  id: 12, date: 1318855706 ,  id: 13, date: 1318929018 ,  id: 14, date: 1318933265 ,  id: 15, date: 1318940511 ,  id: 16, date: 1318945096 ,  id: 17, date: 1319017541 ,  id: 18, date: 1319527136 ,  id: 19, date: 1318582119 ,  id: 20, date: 1318595862 ,  id: 21, date: 1318582119 ,  id: 22, date: 1318595862 ,  id: 23, date: 1319713399 ,  id: 24, date: 1320053428 ,  id: 25, date: 1320333481 ,  id: 26, date: 1320832755 ,  id: 27, date: 1321012378 ,  id: 28, date: 1321280993 ,  id: 29, date: 1321347659 ,  id: 30, date: 1321350476 ,  id: 31, date: 1321369307 ,  id: 32, date: 1321369614 ,  id: 33, date: 1321610123 ,  id: 34, date: 1321613205 ,  id: 35, date: 1321617250 ,  id: 36, date: 1321626603 ,  id: 37, date: 1321865808 ,  id: 38, date: 1321876609 ,  id: 39, date: 1321877598 ,  id: 40, date: 1321877832 ,  id: 41, date: 1321953322 ,  id: 42, date: 1322061969 ,  id: 43, date: 1322142603 ,  id: 44, date: 1322211686 ,  id: 45, date: 1322213793 ,  id: 46, date: 1322214569 ,  id: 47, date: 1322482817 ,  id: 48, date: 1322663742 ,  id: 49, date: 1322664267 ,  id: 50, date: 1322747231 ,  id: 51, date: 1322819964 ,  id: 52, date: 1323358224 ,  id: 53, date: 1323681272 ,  id: 54, date: 1323695093 ,  id: 55, date: 1323696589 ,  id: 56, date: 1323763763 ,  id: 57, date: 1322819964 ,  id: 58, date: 1323681272 ,  id: 59, date: 1323851164 ,  id: 60, date: 1323853123 ,  id: 61, date: 1323854271 ,  id: 62, date: 1323858072 ,  id: 63, date: 1325690573 ,  id: 64, date: 1325751893 ,  id: 65, date: 1325760204 ,  id: 66, date: 1325769098 ,  id: 67, date: 1325769981 ,  id: 68, date: 1325771632 ,  id: 69, date: 1325776473 ,  id: 70, date: 1325837346 ,  id: 71, date: 1326110199 ,  id: 72, date: 1326793097 ,  id: 73, date: 1326878182 ,  id: 74, date: 1326881341 ,  id: 75, date: 1326975873 ,  id: 76, date: 1326985667 ,  id: 77, date: 1327047585 ,  id: 78, date: 1327062945 ,  id: 79, date: 1327063660 ,  id: 80, date: 1327322844 ,  id: 81, date: 1327326904 ,  id: 82, date: 1327329215 ,  id: 83, date: 1327397042 ,  id: 84, date: 1327399839 ,  id: 85, date: 1327401818 ,  id: 86, date: 1327407161 ,  id: 87, date: 1327419420 ,  id: 88, date: 1327570243 ,  id: 89, date: 1327578536 ,  id: 90, date: 1327584554 ,  id: 91, date: 1327914616 ,  id: 92, date: 1327917019 ,  id: 93, date: 1327931685 ,  id: 94, date: 1327933025 ,  id: 95, date: 1327934772 ,  id: 96, date: 1327947074 ,  id: 97, date: 1328626734 ,  id: 98, date: 1328626734 ,  id: 99, date: 1330070074 ,  id: 100, date: 1330073135 ,  id: 101, date: 1330073259 ,  id: 102, date: 1330332445 ,  id: 103, date: 1330351925 ,  id: 104, date: 1330420928 ,  id: 105, date: 1330423209 ,  id: 106, date: 1330437337 ,  id: 107, date: 1330439446 ];

var objPeriodDay = groupByTimePeriod(eventsArray, 'date', 'day');
var objPeriodWeek = groupByTimePeriod(eventsArray, 'date', 'week');
var objPeriodMonth = groupByTimePeriod(eventsArray, 'date', 'month');
var objPeriodYear = groupByTimePeriod(eventsArray, 'date', 'year');

console.log(objPeriodDay);
console.log(objPeriodWeek);
console.log(objPeriodMonth);
console.log(objPeriodYear);

这里是a fiddle to go with it(您必须打开控制台才能看到输出)。

正如您将在小提琴中看到的,4 个对象的键如下:

objPeriodDay: 自 1970 年 1 月 1 日以来的日期 objPeriodWeek: 相对于 1970 年 1 月 1 日第一周的周数 objPeriodMonth: 自 1970 年 1 月 1 日以来的月份数 objPeriodYear:年份

请注意,所有这四种变体都会为您提供唯一键。例如,月份将为您提供“Dec 2011”(以自 1970 年 1 月 1 日以来的月份形式),而不仅仅是“Dec”。

【讨论】:

【参考方案5】:

以上所有解决方案都是庞大的纯 JS 解决方案。如果您可以使用几个库,那么 lodash 和 moment 可以一起使用以形成一个简单的单行:

ES6

let groupedResults = _.groupBy(results, (result) => moment(result['Date'], 'DD/MM/YYYY').startOf('isoWeek'));

旧版 JS

var groupedResults = _.groupBy(results, function (result) 
  return moment(result['Date'], 'DD/MM/YYYY').startOf('isoWeek');
);

这将产生一个以一周开始为键的数组,例如Mon Jul 25 2016 00:00:00 GMT+0100。我相信您可以研究如何扩展它以获得数月、数年等。

回复:@SaintScott 的 cmets

在 cmets 中提到,这并没有直接回答问题,因为原始使用 UTC 时间戳而不是格式化日期。在这种情况下,您应该使用不带第二个参数的moment()

moment(1387271989749).startOf('isoWeek');

或者如果使用 UNIX 时间戳,如下所示:

moment.unix(yourTimestamp).startOf('isoWeek');

...虽然这开始远离问题,并且更多地进入 Moment 文档,如果您想使用这种方法,我建议您阅读。

【讨论】:

这是迄今为止最好的答案 不对,只能用格式字符串构造字符串日期。喜欢moment("12-25-1995", "MM-DD-YYYY") @SaintScott 我知道,result['Date'] 是一个类似于 '25/12/1995' 的字符串。你以为是什么? 最佳答案,简单有效。谢谢! 为什么是isoWeek【参考方案6】:

另一个使用dayjs库的实现

var dayjs = require('dayjs')

function groupTimesBy(theList, unit = 'day', timeParamName = 'create_time')
    var toReturn = ;

    for(let listItem of theList) 
        const paramName = dayjs.unix(listItem[timeParamName]).startOf(unit).unix();
        if(toReturn[paramName] == null) 
            toReturn[paramName] = [];
        
        toReturn[paramName].push(listItem);
    


    return toReturn;


let list = ['time':1609104570, 'time':1609104570];

let byDay = groupTimesBy(list, 'day', 'time');
let byWeek = groupTimesBy(list, 'week', 'time');
let byMonth = groupTimesBy(list, 'month', 'time');
let byYear = groupTimesBy(list, 'year', 'time');

【讨论】:

以上是关于如何按天、周、月对具有时间戳属性的对象进行分组?的主要内容,如果未能解决你的问题,请参考以下文章

SQLAlchemy 在时间戳列中按天分组

如何在打字稿/离子2中按周分组时间戳?

iPhone CoreData:如何按天对获取的结果进行分组?

MySQL按天/周/月/季度/半年/年统计数据

[Mysql] GroupBy 分组,按天周月

如何按月对数组中的值进行分组?