Alexander Demianenko
Alexander Demianenko

Reputation: 124

Split array of objects into groups by week

I have an array of objects that contain data like this

[{
  date: "01-01-2017 00:00:00",
  dataField1: "",
  dataField2: ""
},
{
  date: "01-02-2017 00:00:00",
  dataField1: "",
  dataField2: ""
},
{
  date: "01-15-2017 00:00:00",
  dataField1: "",
  dataField2: ""
},
{
  date: "01-16-2017 00:00:00",
  dataField1: "",
  dataField2: ""
},
{
  date: "01-15-2018 00:00:00",
  dataField1: "",
  dataField2: ""
},
{
  date: "01-16-2018 00:00:00",
  dataField1: "",
  dataField2: ""
}]]

UPD: need to sort by year and month, not just month. I need to split it into an array of arrays where objects are grouped by weeks and years. something like this:

[
  [{
     date: "01-01-2017 00:00:00",
     dataField1: "",
     dataField2: ""
   },
   {
     date: "01-02-2017 00:00:00",
     dataField1: "",
     dataField2: ""
   }
  ],
  [{
     date: "01-15-2017 00:00:00",
     dataField1: "",
     dataField2: ""
   },
   {
     date: "01-16-2017 00:00:00",
     dataField1: "",
     dataField2: ""
   }],
   [
    {
       date: "01-15-2018 00:00:00",
       dataField1: "",
       dataField2: ""
    },
    {
       date: "01-16-2018 00:00:00",
       dataField1: "",
       dataField2: ""
    }
   ]
]

is there a simple way with good performance to make this sorting with javascript?

Upvotes: 3

Views: 4649

Answers (2)

Callam
Callam

Reputation: 11539

If you'll consider using something like momentjs, you can use the format function. This way you can actually group by any of the available date format tokens listed here.

function datesGroupByComponent(dates, token) {
  return dates.reduce(function(val, obj) {
    let comp = moment(obj['date'], 'MM/DD/YYYY').format(token);
    (val[comp] = val[comp] || []).push(obj);
    return val;
  }, {});
}

const dates = [
  { date: "01-01-2017 00:00:00" },
  { date: "01-02-2017 00:00:00" },
  { date: "01-15-2017 00:00:00" },
  { date: "01-16-2017 00:00:00" }
];

/* https://momentjs.com/docs/#/displaying/format */

console.log('D', datesGroupByComponent(dates, 'D')); // Day of Month
console.log('w', datesGroupByComponent(dates, 'w')); // Week of Year
console.log('W', datesGroupByComponent(dates, 'W')); // Week of Year (ISO)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>

Upvotes: 9

I wrestled a bear once.
I wrestled a bear once.

Reputation: 23379

I just so happened to have some code on hand to determine the ISO-8601 Week number.

From there it's just a matter of splitting the date string into usable parts that can be passed to the native Date constructor, then reduce the object down to something with keys so we can put the dates in the correct group, and finally, drop the keys to make it an array, as you requested, using Object.values().

var dates = [{
    date: "01-01-2017 00:00:00",
    dataField1: "",
    dataField2: ""
  },
  {
    date: "01-02-2017 00:00:00",
    dataField1: "",
    dataField2: ""
  },
  {
    date: "01-15-2017 00:00:00",
    dataField1: "",
    dataField2: ""
  },
  {
    date: "01-16-2017 00:00:00",
    dataField1: "",
    dataField2: ""
  }
];

var group = Object.values(dates.reduce((acc, val) => {
  var dateparts = val.date.split(/ |-|:/g);
  var date = new Date(dateparts[2], dateparts[0] - 1, dateparts[1], dateparts[3], dateparts[4], dateparts[5]);
  var weekNo = getISO8601WeekNo(date);
  if (!acc[weekNo]) acc[weekNo] = [];
  acc[weekNo].push(val);
  return acc;
}, {}));

console.log(group);

function getISO8601WeekNo(date) {
  var startDate = new Date(date.getFullYear(), 0);
  var endDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
  while (endDate.getDay() < 6) endDate.setDate(endDate.getDate() + 1);
  endDate = endDate.getTime();
  var weekNo = 0;
  while (startDate.getTime() < endDate) {
    if (startDate.getDay() == 4) weekNo++;
    startDate.setDate(startDate.getDate() + 1);
  }
  return weekNo;
}

Upvotes: 2

Related Questions