Javascript 无法正确排序包含两个数组值的数组

Posted

技术标签:

【中文标题】Javascript 无法正确排序包含两个数组值的数组【英文标题】:Javascript doesn't sort correctly an array including two arrays' values 【发布时间】:2021-11-05 12:57:19 【问题描述】:

我有两个来自 phpjavascript 数组。我将这些数组合并为一个数组。

所有数组的元素都有created_at 值(Laravel)。我想按created_at对这些值进行排序

问题:无论他们的日期如何。第一个数组的元素永远不会出现在第二个数组的后面

示例:B 的日期最晚。但即便如此,C(来自第二个数组)发生在 B 之后。

问题是虽然我将这两个数组合并为一个数组。 Javascript 仍然认为“有两个数组。我应该先排序 数组的元素然后是第二个数组的元素。”

我该怎么做:

history.push(...response.data[0]); // first array's values
history.push(...response.data[1]); // second array's values

history.sort((a, b) => 
            return a.created_at - b.created_at;
          );

所以,历史就像

[

// Comes from first array

 
  name: 'A',
  created_at: '08/09/2021'
 ,

// Comes from first array


  name: 'B',
  created_at: '15/09/2021'
 ,


// This third element comes from second array.


  name: 'C',
  created_at: '08/09/2021'
 
]

我期待这个结果:

新的排序历史:


 
  name: 'A',
  created_at: '08/09/2021'
 ,


  name: 'C',
  created_at: '08/09/2021'
 ,


  name: 'B',
  created_at: '15/09/2021'
 



但 Javascript 最初对第一个数组的元素进行排序。之后,对第二个数组的元素进行排序,结果是:


 
  name: 'A',
  created_at: '08/09/2021'
 ,


  name: 'B',
  created_at: '15/09/2021'
 ,

  name: 'C',
  created_at: '08/09/2021'
 ,

【问题讨论】:

元素的顺序正是你将它们添加到数组中的方式。如果你想对数组进行排序,那么你必须明确地这样做。 您需要按日期对数组进行排序吗?? 你从来没有打电话给history.sort()。你为什么期望它们被重新排序? 合并两个数组后,您需要对其进行排序` history.sort((r1, r2) => d1 = new Date(r1.created_at); d2 = new Date(r2. created_at); 返回 d1 > d2 ? 1 : (d1 JavaScript 在插入时不会自动对数组元素进行排序。不要混淆 ordered set 和 sorted set 的概念。 【参考方案1】:

你可以array#concat你的数组和使用Schwartzian transform通过将created_at转换为YYYY-MM-DD格式来对你的数组进行排序,可以按字典顺序排序。

const arr1 = [name: 'A', created_at: '08/09/2021'],
      arr2 = [name: 'B', created_at: '15/09/2021', name: 'C', created_at: '08/09/2021'],
      result = arr1.concat(arr2)
                   .map(o => [o.created_at.replace(/(..)\/(..)\/(....)/, '$3-$2-$1'), o])
                   .sort((a,b) => a[0].localeCompare(b[0]))
                   .map(([,o]) => o);
console.log(result)

【讨论】:

关于“施瓦茨变换”的 TIL。谢谢! @HassanImam 你能解释或推荐一些关于 Schwartzian Transform 的事情吗?我看过一些网站,但我不明白。 类似于装饰、排序和取消装饰。在装饰步骤中,我们创建了一个原始对象和转换日期的数组,我们将在排序期间使用它,它有助于我们避免多次日期转换。排序后,我们使用 array#map 返回原始对象。【参考方案2】:

您需要向 Array.sort() 发送自定义排序函数

const array = 
[
     name: 'A', created_at: '08/09/2021' ,
     name: 'B', created_at: '15/09/2021' ,
     name: 'C', created_at: '08/09/2021' 
];

const ymd = (dmy) =>  let a = dmy.split('/'); return a[2] + '/' + a[1] + '/' + a[0] 

const sorted = array.sort((elem1, elem2) => ymd(elem1.created_at) > ymd(elem2.created_at) ? 1 : -1);
console.log(sorted);

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

编辑:

如果您需要稳定的排序(这意味着它不会随意交换具有等效比较值的对象)或按日期优先和 alpha 第二,那么您将需要更复杂的排序功能。

用更多的时间和localeCompare() Felix Kling 的建议表,我编写了一个改进的排序,它是稳定的,将按日期排序,然后按名称排序。

const array =
    [
         name: 'A', created_at: '08/09/2021' ,
         name: 'Z', created_at: '15/09/2021' ,
         name: 'D', created_at: '15/09/2021' ,
         name: 'B', created_at: '15/09/2021' ,
         name: 'C', created_at: '08/09/2021', stable: '1',
         name: 'S', created_at: '08/09/2020' ,
         name: 'C', created_at: '08/09/2021', stable: '2' ,
         name: 'C', created_at: '08/06/2021' 
    ];

const ymd = (dmy) =>  let a = dmy.split('/'); return a[2] + '/' + a[1] + '/' + a[0] 

const sorted = array.sort((elem1, elem2) =>
    (ymd(elem1.created_at) === ymd(elem2.created_at)) ?
        elem1.name.localeCompare(elem2.name) :
        ymd(elem1.created_at).localeCompare(ymd(elem2.created_at)));

console.log(sorted);

输出:

[
   name: 'S', created_at: '08/09/2020' ,
   name: 'C', created_at: '08/06/2021' ,
   name: 'A', created_at: '08/09/2021' ,
   name: 'C', created_at: '08/09/2021', stable: '1' ,
   name: 'C', created_at: '08/09/2021', stable: '2' ,
   name: 'B', created_at: '15/09/2021' ,
   name: 'D', created_at: '15/09/2021' ,
   name: 'Z', created_at: '15/09/2021' 
]

【讨论】:

您没有考虑两个日期相等的情况(应该从回调中返回0)。使用ymd(elem1.created_at).localeCompare(ymd(elem2.created_at)) 来简化比较。但到目前为止,您是唯一一个导致原始日期格式不可排序的人。 你是对的,应该有一个稳定的排序 (0) 并且可能也按字母顺序排列,这就是我提到这一点的原因。现在有时间我会努力改进它。【参考方案3】:

您可以尝试先合并数组,然后对结果数组进行排序:

const arr1 = [name: 'A', created_at: '08/09/2021',]
const arr2 = [name: 'B', created_at: '15/09/2021', name: 'C', created_at: '08/09/2021']

const result = arr1.concat(arr2).sort((a , b) => a.created_at > b.created_at)
console.log(result)

【讨论】:

这正是我尝试过的。但我很抱歉我忘了添加排序代码。所以,我让人们头脑混乱。现在我添加了代码。可以看到,虽然 B 有最新的日期,但它仍然在 C(第二个数组)的前面。 两个问题:1).sort需要返回一个负数,0或正数,而不是布尔值。通过返回一个布尔值,您将所有a 小于b 的情况视为a 等于b,这可能导致错误排序。 2)如果您有不同月份或年份的日期,这可能会失败。例如。 22/01/2021 将排在最后。这是因为日期的格式无法按字母数字进行比较。【参考方案4】:

要按日期值排序,您可以使用Date.prototype.getTime():

const arr1 = [name: 'A', created_at: '08/09/2021']
const arr2 = [name: 'B', created_at: '15/09/2021', name: 'C', created_at: '08/09/2021']

const toNumber= d => +d.replace(/\//g, '')
const result =  [...arr1, ...arr2].sort((a, b) => toNumber(a.created_at) - toNumber(b.created_at))

console.log(result)

【讨论】:

为什么会不正确?只是Date 无法处理那种格式。 localeCompare 不起作用,请尝试 "22/01/2021" 选择其中一个日期。 您应该说明您更改了日期格式以及为什么这样做。 你的日期格式错误..你应该试试new Date('15/09/2021'),你会看到错误 这不是我的约会对象。我没有问这个问题。你一直说格式是“错误的”:日期可以用各种方式格式化。仅仅因为Date 无法处理格式并不意味着dd/mm/yyy 是“错误的”。有些国家使用这种格式:en.wikipedia.org/wiki/Date_format_by_country 不客气。我本来可以更清楚为什么日期格式不是“不正确”的。

以上是关于Javascript 无法正确排序包含两个数组值的数组的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript中数组Array.sort()排序方法详解

使用javascript以特定顺序对具有十进制值的数组进行排序

使用Javascript的数组中嵌套对象值的总和

和为定值的两个数

JavaScript算法---快速排序法

Sorted Union