3

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?

  • 3
    `if using lodash then _.groupBy else use lodash` ;) – georg Feb 14 '18 at 13:21
  • If you want to stick to vanilla JS, then you can iterate through the list of data, and put it into an object/map where the key is the number of the week. This isn't a "sorting" thing, this is a "grouping" thing. What have you tried so far? Post some code, please. – TheCrzyMan Feb 14 '18 at 13:35

2 Answers2

9

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>
Callam
  • 11,409
  • 2
  • 34
  • 32
2

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;
}
I wrestled a bear once.
  • 22,983
  • 19
  • 69
  • 116
  • very thanks! you saved me a lot of time! just changed your regex `var dateparts = val.date.split(/ |-|:/g);` for `var dateparts = val.date.split(/T+|-|:/g);` to support date format like this `2017-01-04T00:00:00-05:00` – Alexander Demianenko Feb 14 '18 at 14:22
  • Very you're welcome :D If this did it for you please remember to hit the checkmark to the left to mark it as the accepted answer. @AlexAL – I wrestled a bear once. Feb 14 '18 at 14:30
  • @occams-razor removed checkmark. just got upd. need to sort also by year, not just month. but solving is good :) – Alexander Demianenko Feb 14 '18 at 15:19