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.

3
  • what is the expected output ? Commented Sep 4, 2020 at 18:58
  • that is the input dataset... whats the object format you want for the output Commented Sep 4, 2020 at 19:00
  • I was editing. Ouptut is appended to op. Commented Sep 4, 2020 at 19:02

3 Answers 3

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()));

Sign up to request clarification or add additional context in comments.

1 Comment

This one works great. Just to convert strings back to dates but that wont be a problem. Ty.
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);

1 Comment

Interesting solution! Ty!
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);

1 Comment

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).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.