0

I tried out solutions: here and here but my problem has some additional complications - comparing dates.

Problem: count duplicate Date() objects in JS array. Date objects have different time, but whats important is that date, month and year are the same. For example 3\3\2020 14:30 and 3\3\202 12:21 should be counted as same date.

Dataset(input):

let dates = [new Date(2018, 11, 24, 22),  new Date(2018, 12, 23),       new Date(2013, 02, 3),
             new Date(2018, 11, 24, 22),  new Date(2018, 12, 23, 22),  new Date(2013, 02, 3, 22)]

expected output:

var result = [
         { 'date': dateobj0 , 'count':3; },
         { 'date': dateobj1 , 'count':4; },
         { 'date': dateobj2 , 'count':10; },
         ...
         { 'date': dateobjn , 'count':n; },
        ]

I started by mapping them:

var result = {'date':'', 'count':0};
let mapedDatesList= dates.map(x => {result['date'] = x; return result} )

And after that I tried out to modify\implement solutions listed above with no success. Best I've got is some bubble sort situation, that does not work... . Can someone offer more elegant solution with forEach or map?

let mapedDatesList= dates.map(x => {result['date'] = x;  return result} )

console.log(mapedDatesList)


mapedDatesList.forEach( (x, i) => x.count != 0 ? mapedDatesList[i].count++:  x.count++ )

console.log('-----')

console.log(mapedDatesList)

Totally got stuck in tunnel vision. This situation just counts every time it itterates in forEach in every single object.

Mladen Milosavljevic
  • 1,720
  • 1
  • 12
  • 23

3 Answers3

2

Maybe the following helps: you had some issues with the new Date() expressions. The months are expected as a zero based index, i. e. 0 means January etc..

const dates=[new Date(2018, 11, 23), new Date(2013, 1, 3), new Date(2018, 10, 24, 22), new Date(2018, 11, 23, 22), new Date(2013, 1, 3, 22)];

let dupes=dates.reduce((a,c)=>{
let d=c.toDateString();
a[d]=a[d]?a[d]+1:1;
return a;}, {});

console.log(dupes)

The object dupes currently lists all dates and their count. If you want to exclude the unique dates you could simply filter it again.

Below is the complete solution with the filtered duplicate date objects:

const dates=[new Date(2018, 11, 23), new Date(2013, 1, 3), new Date(2018, 10, 24, 22), new Date(2018, 11, 23, 22), new Date(2013, 1, 3, 22)];

const dupes=Object.entries( 
  dates.reduce((a,c)=>{ // establish counts for all dates
  let d=c.toDateString();
  a[d]=a[d]?a[d]+1:1;
  return a;}, {})
).reduce((a,[d,n])=>{ // revert dupes to date objects again
  if(n>1)a.push(new Date(d));
  return a
},[]);

console.log(dupes.map(d=>d.toDateString()));
Carsten Massmann
  • 26,510
  • 2
  • 22
  • 43
1

An alternative, although more complicated solution would be to do a general group by and then aggregate the groups as needed. This adds flexibility if you ever have structured data that you want to include in your aggregate.

const dates = [
  new Date(2018, 11, 24, 22),
  new Date(2018, 12, 23),
  new Date(2013, 02, 3),
  new Date(2018, 11, 24, 22),
  new Date(2018, 12, 23, 22),
  new Date(2013, 02, 3, 22)
];

const groupBy = (values, selector) => {
  const groups = new Map();
  
  for (let value of values) {
    const key = selector(value);
    const entry = groups.get(key);
    
    if (entry != null) {
        entry.values.push(value);
    } else {
        groups.set(key, { key, values: [value] });
    }
  }
  
  return Array.from(groups.values());
};

const toDay = (date) => {
    return new Date(date.getFullYear(), date.getMonth(), date.getDate());
};

const result = groupBy(dates, (date) => toDay(date).getTime())
  .map(g => {
    return { date: new Date(g.key), count: g.values.length }
  });

console.log(result);

Aggregating over structured data would also be simple.

const data = [
  { date:  new Date(2018, 11, 24, 22), text: 'a'},
  { date:  new Date(2018, 11, 24 ), text: 'b'},
  { date:  new Date(2018, 11, 25, 22 ), text: 'c'},
  { date:  new Date(2018, 11, 24, 9 ), text: 'd'},
];

const dataByDate = groupBy(data, (d) => toDay(d.date).getTime())
  .map((g) => {
    return {
      date: new Date(g.key),
      count: g.values.length,
      values: g.values.map((v) => v.text)
    };
  });
  
console.log(dataByDate);
Brenden
  • 1,947
  • 1
  • 12
  • 10
0
let dates = [new Date(2018, 11, 24, 22),  new Date(2018, 12, 23),new Date(2013, 02, 3),new Date(2018, 11, 24, 22),  new Date(2018, 12, 23, 22),  new Date(2013, 02, 3, 22)];

const count ={};
dates.map(date => {
    date.setTime(0);
    return date;
}).forEach(date => count[date.toDateString()] = count[date.toDateString()] + 1 || 1);
console.log(count);
Moshe Sommers
  • 1,466
  • 7
  • 11
  • Code–only answers aren't that helpful. An answer should explain the issue and how to fix it. This modifies the supplied dates, which likely is an unexpected and unwanted side effect. It also sets them all to 1 Jan 1970 (*setTime* doesn't set the time component, it sets the time value that is the offset from the epoch). – RobG Sep 04 '20 at 21:43