排序"年/月"字符串作为降序日期

时间:2017-05-18 15:19:08

标签: javascript sorting date

我有一个对象数组,其中包含"年/月"作为字符串值。我将它们作为日期对象进行比较,但问题是如果我比较年份和第一个月它会失败,因为它在代码中:" 2015"和" 2015/1"



const sortYearsMonthsDescending = (items) => items.sort((a, b) => (new Date(a.value) - new Date(b.value)));

const toBeSortedWorking = [{value: "2015"}, {value:"2015/3"}, {value: "2015/10"}];
const toBeSortedNotWorking = [{value: "1933"}, {value:"1933/11"}, {value: "1933/1"}];
console.log(sortYearsMonthsDescending(toBeSortedWorking));
console.log(sortYearsMonthsDescending(toBeSortedNotWorking));




5 个答案:

答案 0 :(得分:0)

let dates = ['2012','2018/1','2018','2014/3','2015','2012/5','2019','2013/7','2011/3','2010/10','2039/3'];

function compareDates(date1, date2) {
  for (let i = 0; i < Math.max(date1.length, date2.length); i++) {
    if (date1[i] > date2[i]) return 1;
    else if (date1[i] < date2[i]) return -1;
    else if (!date1[i] && date2[i]) return -1;
    else if (date1[i] && !date2[i]) return 1;
  }
  return 0;
}

console.log(dates.sort(compareDates));

答案 1 :(得分:0)

您可以轻松完成,因为Datecomparable object Compare two dates with JavaScript

const a = [{value: "2015"}, {value:"2015/3"}, {value: "2015/10"}];
const b = [{value: "1932"}, {value:"1933/11"}, {value: "1932/1"}];

function sort(items) {
  
  return items
    .sort((a, b) => {
      a = a.value.split('/');
      b = b.value.split('/');
      
      // months at Index = 1, consider as 0 based
      a[1] = a[1] ? (a[1] - 1) : 0;
      b[1] = b[1] ? (b[1] - 1) : 0;
      
      /* ASC */ return (new Date(...a)) - (new Date(...b)); 
      /* DESC */ return (new Date(...b)) - (new Date(...a)); 
    })
  ;
}

console.log('a = ', sort(a));
console.log('b = ', sort(b));

答案 2 :(得分:0)

无法以区分提供月份值的方式初始化日期,因为它为所有人使用默认值以便能够创建有效日期。

在这个特定的用例中,我认为你可以在日期字符串的长度上添加二次计算,如下所示:

const sortYearsMonthsDescending = (items) => items.sort((a, b) => 
  (new Date(a.value) - new Date(b.value))) || (a.value.length - b.value.length);

基本上,这是一个边缘情况,"2015""2015/1"之间存在歧义。我们用语句&#34来解决这种歧义:在较长的字符串之前取较短的字符串&#34;。

答案 3 :(得分:0)

请注意,下面我还列出了可扩展性的日期。如果根本不需要,则可以轻松删除。

解决方案1 ​​ - 字符串排序键YYYY-MM-DD-LLLLL

以下解决方案创建了一个不使用Date对象进行排序的密钥,并且还考虑到应该按升序排序顺序处理没有​​月份或日期的字符串。

排序键是:YYYY-MM-DD-LLLLL其中L是长度。

&#13;
&#13;
  const ensureDigits = (value, digits) => {
    return (Math.pow(10, digits) + value).toString().slice(- digits);
  };
  
  const sortDateStrings = (dateStrLst, descending) => {
    
    return dateStrLst
      .map(dateStr => {
        const parts = dateStr.value.split('/');
        const year = parts[0];
        const month = ensureDigits(parts[1] || 1, 2);
        const day = ensureDigits(parts[2] || 1, 2);
        const length = ensureDigits(dateStr.value.length, 5);
        return { item: dateStr, sortKey: `${year}-${month}-${day}-${length}` }
      })
      .sort((model1, model2) => 
        model1.sortKey.localeCompare(model2.sortKey) * (descending ? -1 : 1))
      .map(model => model.item);
  }
  
  const dateStrLst1 = [{value: "2015"}, {value:"2015/3"}, {value: "2015/10"}];
  const dateStrLst2 = [{value: "1932"}, {value:"1933/11"}, {value: "1932/1"}];
  
  console.log(sortDateStrings(dateStrLst1).map(item => item.value));
  console.log(sortDateStrings(dateStrLst2).map(item => item.value));
  
  console.log(sortDateStrings(dateStrLst1, true).map(item => item.value));
  console.log(sortDateStrings(dateStrLst2, true).map(item => item.value));
&#13;
&#13;
&#13;

解决方案2 - 按日期排序,然后按长度排序,使用Lodash

&#13;
&#13;
  const sortDateStrings = (dateStrLst, descending) => {
    
    const model = dateStrLst
      .map(dateStr => {
        const parts = dateStr.value.split('/');
        const year = parts[0];
        const month = (parts[1] - 1) || 0;
        const day = parts[2] || 1;
        return { item: dateStr, date: new Date(year, month, day), length: dateStr.value.length };
      });
      
      const direction = descending ? 'desc' : 'asc';
    
    return _.orderBy(model, ["date", "length"], [direction, direction])
      .map(modelItem => modelItem.item);
  };
  
  const dateStrLst1 = [{value: "2015"}, {value:"2015/3"}, {value: "2015/10"}];
  const dateStrLst2 = [{value: "1932"}, {value:"1933/11"}, {value: "1932/1"}];
  
  console.log(sortDateStrings(dateStrLst1).map(item => item.value));
  console.log(sortDateStrings(dateStrLst2).map(item => item.value));
  
  console.log(sortDateStrings(dateStrLst1, true).map(item => item.value));
  console.log(sortDateStrings(dateStrLst2, true).map(item => item.value));
&#13;
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <link rel="stylesheet" href="style.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.2/lodash.js"></script>
  </head>

  <body>
  </body>
</html>
&#13;
&#13;
&#13;

警告:解析日期

// valid ISO 8601 string
const date1 = new Date(Date.parse('1932'));
Fri Jan 01 1932 02:00:00 GMT+0200 (GTB Standard Time)

// not a valid string
const date2 = new Date(Date.parse('1932/1'));
Fri Jan 01 1932 00:00:00 GMT+0200 (GTB Standard Time)

// not a valid string
const date3 = new Date(Date.parse('1932/01'));
Fri Jan 01 1932 00:00:00 GMT+0200 (GTB Standard Time)

date1.getTime() == date2.getTime();
false

也是如此:

// not a valid ISO 8601 string
const date2 = new Date(Date.parse('1932-1'));
Fri Jan 01 1932 00:00:00 GMT+0200 (GTB Standard Time)

但这个是正确的:

// valid ISO 8601 string
const date3 = new Date(Date.parse('1932-01'));
Fri Jan 01 1932 02:00:00 GMT+0200 (GTB Standard Time)

同样的问题也发生在:

// incorrect usage of constructor
new Date(1932);
Thu Jan 01 1970 02:00:01 GMT+0200 (GTB Standard Time)

// correct usage of constructor
new Date(1932, 0);
Fri Jan 01 1932 00:00:00 GMT+0200 (GTB Standard Time)

但最后一个new Date(1932)不正确,因为构造函数至少允许{+ 1}}中的年份和月份。

请注意,在我使用new Date(year, month[, date[, hours[, minutes[, seconds[, milliseconds]]]]]);之前,因为:

  

注意:由于浏览器差异和不一致,强烈建议不要使用Date构造函数(和Date.parse,它们是等效的)解析日期字符串。

new Date(Date.parse(...))可以解析:

  

表示RFC2822ISO 8601日期的字符串(可能会使用其他格式,但结果可能会出乎意料)。

关于这个问题,ISO 8601格式是(注意日期和月份只有两位数字):

Date

关于这个问题,RFC2822格式是:

   Year:
      YYYY (eg 1997)
   Year and month:
      YYYY-MM (eg 1997-07)
   Complete date:
      YYYY-MM-DD (eg 1997-07-16)

因此,我找不到匹配类似date-time = [ day-of-week "," ] date FWS time [CFWS] date = day month year 的内容的格式,以便在浏览器之间进行一致的解析。

性能方面可能是坚持1932/1格式,通常是第一个被检查的格式,然后是ISO 8601

如果您使用 momentjs ,请检查parsing strings section是否支持您要解析的格式。

<强>结论:

始终如一地创建日期对象以获得正确的结果:

  • 日期构造函数:(提供本地日期) - RFC2822
  • Date.parse()(仅在与ISO 8601日期一起使用时生成UTC日期) - new Date(1932, 0)Date.parse('1932')相同 - 请注意月份的两位数

Date.parse('1932-01')给出UTC日期(仅限日期),因为:

  

对ISO 8601格式的支持不同之处在于,仅限日期的字符串(例如&#34; 1970-01-01&#34;)被视为UTC,而不是本地。

答案 4 :(得分:0)

您只需要以易于比较的格式获取字符串,无论是字符串还是日期。但是,使用日期会增加一个问题,即如何确保1933年在1932/12和1933/1之间正确排序。

以下通过附加&#34; / 00&#34;作为字符串排序任何只有一年的价值,并将前导零加到单个数字的月份,以便&#34; 2015/3&#34;比较为&#34; 2015/03&#34;。

&#13;
&#13;
var values  = [{value: "2015"}, {value:"2015/3"}, {value: "2015/10"}, {value: "1933"}, {value: "1932/12"}, {value:"1933/11"}, {value: "1933/1"}];

console.log(JSON.stringify(values));

values.sort(function(a, b) {
  function fixMonth(date) {
    var t = date.split('/');
    return t[0] + '/' + ('0'+t[1]).slice(-2); 
  }
  a = /\//.test(a.value)? fixMonth(a.value) : a.value + '/00';
  b = /\//.test(b.value)? fixMonth(b.value) : b.value + '/00';
  return a.localeCompare(b);
})

console.log(JSON.stringify(values));
&#13;
&#13;
&#13;

如果需要降序排序,只需反转结果。

另一个版本是:

&#13;
&#13;
var values  = [{value: "2015"}, {value:"2015/3"}, {value: "2015/10"}, {value: "1933"}, {value: "1932/12"}, {value:"1933/11"}, {value: "1933/1"}];

values.sort(function(a, b) {
  a = /\//.test(a.value) ? a.value.replace(/(\/)(\d)$/, "$10$2") : a.value + '/00';
  b = /\//.test(b.value) ? b.value.replace(/(\/)(\d)$/, "$10$2") : b.value + '/00';
  return a.localeCompare(b);
})

console.log(JSON.stringify(values));
&#13;
&#13;
&#13;

甚至:

&#13;
&#13;
var values  = [{value: "2015"}, {value:"2015/3"}, {value: "2015/10"}, {value: "1933"}, {value: "1932/12"}, {value:"1933/11"}, {value: "1933/1"}];

values.sort(function(a, b) {
  function resolve(s) {
    return /\//.test(s) ? s.replace(/(\/)(\d)$/, "$10$2") : s + '/00';
  }
  return resolve(a.value).localeCompare(resolve(b.value));
})

console.log(JSON.stringify(values));
&#13;
&#13;
&#13;