Javascript неправильно сортирует массив, содержащий значения двух массивов

#javascript #arrays #vue.js

Вопрос:

У меня есть два массива в Javascript, исходящие из Php. Я объединяю эти массивы в один массив.

Все элементы массивов имеют 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'
 },

 

Комментарии:

1. Порядок элементов — это именно то, как вы добавляете их в массив. Если вы хотите отсортировать массив, вам нужно явно это сделать.

2. Вам нужно отсортировать массив по дате ??

3. Ты так и не позвонил history.sort() . Почему вы ожидаете, что они будут переупорядочены?

4. После объединения обоих ваших массивов вам нужно отсортировать их по history.sort((r1, r2) => { d1 = новая дата(r1.created_at); d2 = новая дата(r2.created_at); возврат d1 >> d2 ? 1 : (d1

5. JavaScript не выполняет автоматическую сортировку элементов массива при вставке. Не путайте понятие упорядоченного множества и отсортированного множества.

Ответ №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) 

Комментарии:

1. До «преобразования Шварца». Спасибо!

2. @HassanImam Можете ли вы объяснить или порекомендовать некоторые вещи о преобразовании Шварца. Я просмотрел несколько сайтов, но ничего не понял.

3. Это похоже на украшение, сортировку и отделку. На этапе декорирования мы создаем массив исходного объекта и преобразованной даты, который мы будем использовать при сортировке, это помогает нам избежать многократного преобразования дат. После сортировки мы возвращаем исходный объект с помощью 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

Редактировать:

Если вам нужна стабильная сортировка (то есть она никогда произвольно не меняет объекты местами с эквивалентными значениями сравнения) или по дате первой и альфа-второй, вам понадобится более сложная функция сортировки.

Имея немного больше времени и localeCompare() форму предложения Феликса Клинга, я написал улучшенную сортировку, которая стабильна и будет сортироваться по дате и имени во-вторых.

 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' }
]
 

Комментарии:

1. Вы не учитываете случай, когда обе даты равны (и должны возвращаться 0 после обратного вызова). Используйте ymd(elem1.created_at).localeCompare(ymd(elem2.created_at)) для упрощения сравнения. Но пока вы единственный, кто объясняет, что исходный формат даты не поддается сортировке.

2. Вы правы, должна быть стабильная сортировка (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) 

Комментарии:

1. Это именно то, что я пробовал. Но мне так жаль, что я забыл добавить код сортировки. Итак, я сбил людей с толку. Теперь я добавил код. Как вы можете видеть, хотя у B есть последняя дата, она все еще находится перед C (второй массив).

2. Две проблемы: 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) 

Комментарии:

1. Почему это должно быть неверно? Просто Date я не могу справиться с этим форматом. localeCompare не получится, попробуй "22/01/2021" на одно из свиданий.

2. Вы должны объяснить, что вы изменили формат даты и почему вы это сделали.

3. Формат вашей даты неверен.. Вы должны попробовать new Date('15/09/2021') , и вы увидите ошибку

4. Это не мое свидание. Я не задавал этого вопроса. Вы продолжаете говорить, что формат «неправильный»: даты могут быть отформатированы различными способами. Только потому Date , что не может обработать формат, не означает dd/mm/yyy , что это «неправильно». Есть страны, которые используют этот формат: en.wikipedia.org/wiki/Date_format_by_country

5. Пожалуйста. Я мог бы яснее объяснить, почему формат даты не является «неправильным».