我经常看到这个问题经常用于常规javascript数组,但是如果它的日期数组似乎没有一个答案可行。
我可以通过试验错误地解决这个问题,但如果我问的话,我确实看到了其他人的好处。
基本上,如果你有一个日期的javascript数组可能有重复项并且需要过滤到没有重复项的数组中,那么最好的方法是什么?
我尝试过Array.from(new Set(arr))
的ES6解决方案,但它只返回相同的数组。
我也试过
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;
}
但是没有效果,看起来indexOf
对日期对象不起作用。
以下是我的数组如何生成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;
})
大约有100个左右不同的日子,但它总是以大约350个索引结束。
答案 0 :(得分:10)
如果您通过===
比较两个日期,则比较两个日期对象的引用。表示相同日期的两个对象仍然是不同的对象。
相反,请比较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);
优化和错误处理取决于您。
答案 1 :(得分:5)
您可以使用查找执行简单的过滤器,但是您需要将日期转换为可以比较的内容,因为JavaScript中的两个对象永远不会相同,除非它对完全相同的对象进行了两次引用。
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));
&#13;
如果您想通过更改确定唯一性的方式来过滤任何,这可以进一步推广
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));
&#13;
这是使用ES6,但转换为符合ES5的代码很简单 - 删除胖箭头功能,默认参数和new Set()
就是您所需要的:
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;
}
})
}
答案 2 :(得分:3)
ES6
方式:
datesArray.filter((date, i, self) =>
self.findIndex(d => d.getTime() === date.getTime()) === i
)
答案 3 :(得分:2)
日期问题是运营商===
和!==
无法按预期工作(即他们比较指针而不是实际值)。
一种解决方案是使用Underscore's uniq函数和自定义转换函数来比较值:
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();
})
答案 4 :(得分:1)
基于其他一些答案,我确实是这样的(ES6 +):
const uniqueDates = [...new Set(dateInputArray.map(r => r.getTime()))].map((r: number)=>(new Date(r)));
使用getTime()创建一组唯一的日期转换为数字,然后将它们映射回Date对象数组。
答案 5 :(得分:0)
您可以使用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);