Unique Array for dates Javascript
You can do a simple filter with a lookup but you need to convert the dates to something that can be compared, since two objects are never the same in JavaScript, unless it's two references to the exact 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 generalised, if you want to filter anything by just changing how you determine 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's 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's trivial to convert to ES5 compliant code - removing the fat arrow functions, the default parameter and the new Set()
here 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;
}
})
}
If you compare two dates via ===
, you compare the references of the two date objects. Two objects that represent the same date still are different objects.
Instead, compare the 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.
ES6
way:
datesArray.filter((date, i, self) =>
self.findIndex(d => d.getTime() === date.getTime()) === i
)
Thanks to https://stackoverflow.com/a/36744732/3161291