Unique Array for Javascript Dates

I see that this question was asked quite often for regular javascript arrays, however, none of the answers work if its an array of dates.

I can probably figure this out through a trial error, but I see some advantages for others, if I ask.

Basically, if you have an array of javascript dates that can have duplicates, and you need to filter them into an array without duplicates, which is best for this?

I tried the ES6 solution Array.from Array.from(new Set(arr)) , but it just returns the same array.

Also i tried

 Array.prototype.unique = function() { var a = []; for (var i=0, l=this.length; i<l; i++) if (a.indexOf(this[i]) === -1) a.push(this[i]); return a; } 

both came from unique values ​​in the array

However, none of them work, it looks like indexOf does not work with date objects.

This is how my array is created by atm

 //this is an array generated from ajax data, //its a year over year comparison with a separate year, //so to create a reliable date objects I force it to use the same year. data.map(d => { dp = new Date(Date.parse(d.date + '-' + d.year)); dp.setFullYear(2000); return dp; }) 

It's about 100 or so different days, but always ends with about 350 indexes.

+5
source share
5 answers

If you compare two dates via === , you are comparing the links of two date objects. Two objects that represent the same date are still different objects.

Instead, compare timestamps from Date.prototype.getTime() :

 function isDateInArray(needle, haystack) { for (var i = 0; i < haystack.length; i++) { if (needle.getTime() === haystack[i].getTime()) { return true; } } return false; } var dates = [ new Date('October 1, 2016 12:00:00 GMT+0000'), new Date('October 2, 2016 12:00:00 GMT+0000'), new Date('October 3, 2016 12:00:00 GMT+0000'), new Date('October 2, 2016 12:00:00 GMT+0000') ]; var uniqueDates = []; for (var i = 0; i < dates.length; i++) { if (!isDateInArray(dates[i], uniqueDates)) { uniqueDates.push(dates[i]); } } console.log(uniqueDates); 

Optimization and error handling is up to you.

+5
source

You can make a simple filter with a search, but you need to convert dates to something that can be compared, since two objects are never the same in JavaScript, unless it refers to the same object.

 const dates = [ new Date(2016, 09, 30, 10, 35, 40, 0), new Date(2016, 09, 30, 10, 35, 40, 0), //same new Date(2016, 09, 30, 10, 35, 40, 0), //same new Date(1995, 07, 15, 03, 15, 05, 0) //different ]; function filterUniqueDates(data) { const lookup = new Set(); return data.filter(date => { const serialised = date.getTime(); if (lookup.has(serialised)) { return false; } else { lookup.add(serialised); return true; } }) } console.log(filterUniqueDates(dates)); 

This can be further generalized if you want to filter something by simply changing the way you define uniqueness.

 const dates = [ new Date(2016, 09, 30, 10, 35, 40, 0), new Date(2016, 09, 30, 10, 35, 40, 0), //same new Date(2016, 09, 30, 10, 35, 40, 0), //same new Date(1995, 07, 15, 03, 15, 05, 0) //different ]; const dateSerialisation = date => date.getTime(); // this is the previous logic for dates, but extracted //as primitives, these can be compared for uniqueness without anything extra const numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]; const strings = ["a", "b", "b", "c", "c", "c"]; const people = [ {name: "Alice", age: 20}, {name: "Bob", age: 30}, {name: "Bob", age: 40}, //technically the same {name: "Carol", age: 50}, {name: "Carol", age: 60}, //technically the same {name: "Carol", age: 70} //technically the same ] //let assume that a person with the same name is the same person regardless of anything else const peopleSerialisation = person => person.name; /* * this now accepts a transformation function that will be used * to find duplicates. The default is an identity function that simply returns the same item. */ function filterUnique(data, canonicalize = x => x) { const lookup = new Set(); return data.filter(item => { const serialised = canonicalize(item); //use extract the value by which items are considered unique if (lookup.has(serialised)) { return false; } else { lookup.add(serialised); return true; } }) } console.log("dates", filterUnique(dates, dateSerialisation)); console.log("numbers", filterUnique(numbers)); console.log("strings", filterUnique(strings)); console.log("people", filterUnique(people, peopleSerialisation)); 

This is using ES6, but it is trivial to convert to ES5-compatible code - removing the fat arrow functions, the default parameter and new Set() is what you need:

 function filterUnique(data, canonicalize) { if (!canonicalize) { canonicalize = function(x) { return x; } } var lookup = {}; return data.filter(function(item) { var serialised = canonicalize(item); if (lookup.hasOwnProperty(serialised)) { return false; } else { lookup[serialised] = true; return true; } }) } 
+3
source

The problem with dates is that the === and !== operators do not work as expected (i.e. compare pointers instead of actual values).

One solution is to use the Underscore uniq function with a special conversion function to compare values:

 var dates = data.map(d => { dp = new Date(Date.parse(d.date + '-' + d.year)); dp.setFullYear(2000); return dp; }) var unique = _.uniq(dates, false, function (date) { return date.getTime(); }) 
+1
source

ES6 way:

 datesArray.filter((date, i, self) => self.findIndex(d => d.getTime() === date.getTime()) === i ) 

Thanks to fooobar.com/questions/47182 / ...

0
source

You can use Array.prototype.reduce () :

 const dates = [ new Date(2016, 09, 30, 10, 35, 40, 0), new Date(2016, 09, 30, 10, 35, 40, 0), // same new Date(2016, 09, 30, 10, 35, 40, 0), // same new Date(1995, 07, 15, 03, 15, 05, 0) // different ]; const uniqueDates = dates.reduce((a, c) => { !a.hash[c] && a.result.push(c); a.hash[c] = true; return a; }, {result: [], hash: {}}); console.log(uniqueDates.result); 
0
source

Source: https://habr.com/ru/post/1259053/


All Articles