独特的日期数组Javascript

时间:2016-10-31 16:51:32

标签: javascript arrays date

我经常看到这个问题经常用于常规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;
}

均来自Unique values in an array

但是没有效果,看起来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个索引结束。

6 个答案:

答案 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中的两个对象永远不会相同,除非它对完全相同的对象进行了两次引用。

&#13;
&#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
];


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;
&#13;
&#13;

如果您想通过更改确定唯一性的方式来过滤任何,这可以进一步推广

&#13;
&#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;
&#13;
&#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
)

感谢https://stackoverflow.com/a/36744732/3161291

答案 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);