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

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

所有数组的元素都有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 的概念。

标签: javascript arrays vue.js


【解决方案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
  • 不客气。我本来可以更清楚为什么日期格式不是“不正确”的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-02
  • 1970-01-01
  • 2019-07-21
  • 2015-07-02
  • 2016-01-11
  • 2012-01-09
相关资源
最近更新 更多