mario
mario

Reputation: 112

Javascript: creating an array out of another

I have an array-snapshot out of Firebase, which contains the entries of my application. Among other information there is a a timestamp called "referenceDate":

entry [0] { referenceDate: 2017-08-03,...
entry [1] { referenceDate: 2017-08-02,...
entry [2] { referenceDate: 2017-08-01,...
entry [3] { referenceDate: 2017-07-03,...
entry [4] { referenceDate: 2017-07-02,...

I want to output the entries grouped under a headline by month and year like this

08.2017
03.08.2017
02.08.2017
01.08.2018
07.2017
03.07.2017
02.07.2017

My idea is to loop over the array-snapshot and create another nested one which looks like this:

 {"monthYear": "08.2017":[
    {"referenzDatum": 2017-08-03},... },
    {"referenzDatum": 2017-08-02},... },
    {"referenzDatum": 2017-08-01},... },]},
 {"monthYear": "07.2017":[
    {"referenzDatum": 2017-07-03},... },
    {"referenzDatum": 2017-07-02},... }, ...]}

Then loop over it with two nested ngFor to generate the html output.

Is there a simple way to achieve this? I tried to just push the old entry into the new array according to the corresponding monthyear whenever this changes, but that didnt work out, because references were copied. then I tried the following code, but it doesn't really work for more than 2 different months and the code just looks awful.

var oldDate: string;
var newDate: string;
sortedLogLine.entries = [];
this.sortedLog = [];

for (var i = 0; i < entries.length; i++) {
  newDate = entries[i].referenceDate.substring(0, 7).split('-').reverse().join('.');
  if (oldDate == newDate) {
    sortedLogLine.entries.push(entries[i]);
  }
  if (!oldDate) {
    sortedLogLine.entries.push(entries[i]);
    sortedLogLine.monthYear = newDate;
    oldDate = newDate;
  }
  if (oldDate != newDate) {

    pushSortedLogLine.entries = sortedLogLine.entries.slice(0);
    pushSortedLogLine.monthYear = oldDate;
    this.sortedLog.push(pushSortedLogLine);

    sortedLogLine.entries = [];
    sortedLogLine.entries.push(entries[i]);
    sortedLogLine.monthYear = newDate;
    oldDate = newDate;
  }
}
this.sortedLog.push(sortedLogLine);

Any suggestions as to how to do this more efficiently?

Upvotes: 2

Views: 75

Answers (5)

Asher Dale
Asher Dale

Reputation: 149

Try this:

const entries = [{ referenceDate: '2017-08-03'}, { referenceDate: '2017-08-02'}, { referenceDate: '2017-08-01'}, { referenceDate: '2017-07-03'}, { referenceDate: '2017-07-02'}]; 
const resultObj = {};

for (var i = 0; i < entries.length; i++) {
  const month = entries[i].referenceDate.substring(0, 7).split('-').reverse().join('.');
  if (!resultObj[month]) {
  	resultObj[month] = [];
  }
  resultObj[month].push(entries[i]);
}

const out = document.getElementsByClassName('json')[0];
out.innerHTML = JSON.stringify(resultObj, undefined, 2);
<pre class="json"></pre>

Upvotes: 0

Oyeme
Oyeme

Reputation: 11235

 var arrayOfDates = [
      {'referenceDate':'2017-08-03'},
      {'referenceDate':'2017-08-02'},
      {'referenceDate':'2017-08-01'},
      {'referenceDate':'2017-07-03'},
      {'referenceDate':'2017-07-02'}
   ];
  var result = [];

arrayOfDates.forEach(function(entry) {
    var date = new Date(entry.referenceDate);

    var day = date.getDate();
    var monthIndex = date.getMonth() +1;
    var year = date.getFullYear();

    var keyDateFormat = monthIndex+'.'+year;

    if(!(keyDateFormat in result)) {
        result[keyDateFormat] = [];
    }
    result[keyDateFormat].push(entry);
});

Upvotes: 1

Jonas Wilms
Jonas Wilms

Reputation: 138567

Probably use a hash table:

var timetable = {};

data.forEach(function(el){
  var d = new Date(el.referenceDate),
  y = d.getFullYear(),
  m = d.getMonths() +1;

  if(!timetable[y]) timetable[y] = [];
  if(!timetable[y][m]) timetable[y][m] = [];

  timetable[y][m].push(el);
});

Then you can get all available years, sort and display them:

Object.values(timetable).sort((a,b)=>a-b).forEach(function(year){
  console.log(year);
  for(var [month,entries] of timetable[year].entries()){
     console.log(month,entries.join("\n"));
  }
});

Upvotes: 0

Mark Reed
Mark Reed

Reputation: 95395

The usual way to do something like this would be groupBy. Javascript doesn't have a native groupBy method on its Arrays, but several helper libraries like underscore.js do. Alternatively, you can roll your own with reduce:

let getMonthYear = (obj) => obj.referenceDate.split('-').reverse().slice(1).join('.');

let groupBy = (ary, fn) => ary.reduce(function(groups, item) {
       let key = fn(item)
       if (!groups[key]) groups[key]=[]
       groups[key].push(item)
       return groups;
   }, {});

let entries = [ { referenceDate: '2017-08-03' },
                { referenceDate: '2017-08-02' },
                { referenceDate: '2017-08-01' },
                { referenceDate: '2017-07-03' },
                { referenceDate: '2017-07-02' } ]
let groups = groupBy(entries, getMonthYear);
console.log(groups)

which produces this output:

{ '08.2017': 
   [ { referenceDate: '2017-08-03' },
     { referenceDate: '2017-08-02' },
     { referenceDate: '2017-08-01' } ],
  '07.2017': 
   [ { referenceDate: '2017-07-03' },
     { referenceDate: '2017-07-02' } ] }

See also this similar question.

Upvotes: 1

Andrei Matracaru
Andrei Matracaru

Reputation: 3671

Have a look at Lodash, specifically groupBy

I believe Lodash is a better choice over Underscore.js, more details in this blog post

Upvotes: 0

Related Questions