Сортировать массив объектов по одному ключу со значением даты


257

У меня есть массив объектов с несколькими парами ключ-значение, и мне нужно отсортировать их на основе «updated_at»:

[
    {
        "updated_at" : "2012-01-01T06:25:24Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-09T11:25:13Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-05T04:13:24Z",
        "foo" : "bar"
    }
]

Какой самый эффективный способ сделать это?


@Topener Эта ссылка выглядит так, как будто это вопрос к PHP
Дэвид Брейнер

моя ошибка .. не правильно прочитал
Рене Пот

Ответы:


339

Вы можете использовать Array.sort.

Вот пример:

var arr = [{
    "updated_at": "2012-01-01T06:25:24Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-09T11:25:13Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-05T04:13:24Z",
    "foo": "bar"
  }
]

arr.sort(function(a, b) {
  var keyA = new Date(a.updated_at),
    keyB = new Date(b.updated_at);
  // Compare the 2 dates
  if (keyA < keyB) return -1;
  if (keyA > keyB) return 1;
  return 0;
});

console.log(arr);


17
Не могли бы вы использовать keyA - keyB(или, возможно, keyB - keyA)? У объектов даты есть valueOf()метод.
soktinpk

вернуть updated_at <b. updated_at? 1: -1 у меня работает. Не нужно разбирать в формате даты.
oliversisson

Способность просто делать a-bдействительно важна, если вы хотите избежать длинного кода. Хотя длинный код не обязательно плох в этом случае, я думаю, что многословие усложняет понимание. Я сейчас пользуюсь values.sort((a,b)=>a.attr-b.attr). Необходимость писать 5 строк каждый раз, когда вам нужно отсортировать массив, становится утомительной.
AnnanFay

Этот пример неэффективен и выполняет много ненужных вычислений. Сортировка может принимать любое положительное или отрицательное число в качестве действительного результата. Ваши дополнительные вычисления, чтобы заставить его быть 1,0, -1 не нужны и просто добавляют дополнительные вычисления за выполнение.
Патрик В. МакМахон

Прекрасно работает, спасибо
Энди

158

Я уже ответил на очень похожий вопрос: простая функция для сортировки массива объектов

Для этого вопроса я создал эту маленькую функцию, которая может делать то, что вы хотите:

function sortByKey(array, key) {
    return array.sort(function(a, b) {
        var x = a[key]; var y = b[key];
        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });
}

11

2
Как бы вы изменили это?

4
Чтобы сделать его нечувствительным к регистру, вы можете добавить .toLowerCase () к переменным x и y
Джейкоб ван Линген

4
Чтобы поменять функции сортировки, подобные этим, просто умножьте результат на -1:)
Svish

5
Или просто взять вывод и использовать array = array.reverse()функцию.
Люк Стивенсон

31

Метод Array.sort () сортирует элементы массива на месте и возвращает массив. Будьте осторожны с Array.sort (), так как он не является неизменным . Для неизменной сортировки используйте immutable-sort .

Этот метод сортирует массив, используя ваш текущий updated_atформат ISO. Мы используем new Data(iso_string).getTime()для преобразования времени ISO в метку времени Unix. Метка времени Unix - это число, на котором мы можем сделать простую математику. Мы вычитаем первую и вторую временные метки; если первая временная метка больше второй, возвращаемое число будет положительным. Если второе число больше первого, возвращаемое значение будет отрицательным. Если два одинаковы, возврат будет равен нулю. Это прекрасно согласуется с необходимыми возвращаемыми значениями для встроенной функции.

Для ES6 :

arr.sort((a,b) => new Date(a.updated_at).getTime() - new Date(b.updated_at).getTime());

Для ES5 :

arr.sort(function(a,b){ 
 return new Date(a.updated_at).getTime() - new Date(b.updated_at).getTime();
});

Если вы измените свои updated_atметки времени на Unix, вы можете сделать это:

Для ES6 :

arr.sort((a,b) => a.updated_at - b.updated_at);

Для ES5 :

arr.sort(function(a,b){ 
 return a.updated_at - b.updated_at;
});

На момент написания этой статьи современные браузеры не поддерживают ES6. Для использования ES6 в современных браузерах используйте babel для переноса кода в ES5. Ожидайте поддержку браузера для ES6 в ближайшем будущем.

Array.sort () должен получить возвращаемое значение одного из 3 возможных результатов:

  • Положительное число (первый элемент> второй элемент)
  • Отрицательное число (первый элемент <второй элемент)
  • 0, если два элемента равны

Обратите внимание, что возвращаемое значение встроенной функции может быть любым положительным или отрицательным числом. Array.Sort () не волнует, что такое возвращаемый номер. Это заботится только, если возвращаемое значение является положительным, отрицательным или нулевым.

Для неизменной сортировки: (пример в ES6)

const sort = require('immutable-sort');
const array = [1, 5, 2, 4, 3];
const sortedArray = sort(array);

Вы также можете написать это так:

import sort from 'immutable-sort';
const array = [1, 5, 2, 4, 3];
const sortedArray = sort(array);

Импортирование, которое вы видите, является новым способом включения JavaScript в ES6 и делает ваш код очень чистым. Мой личный фаворит.

Неизменяемая сортировка не изменяет исходный массив, а возвращает новый массив. Использование constрекомендуется на неизменных данных.


19

Вот слегка измененная версия ответа @David Brainer-Bankers, который сортирует по алфавиту по строке или по номеру и гарантирует, что слова, начинающиеся с заглавных букв, не сортируются над словами, начинающимися со строчной буквы (например, «apple, Early») будет отображаться в этом порядке).

function sortByKey(array, key) {
    return array.sort(function(a, b) {
        var x = a[key];
        var y = b[key];

        if (typeof x == "string")
        {
            x = (""+x).toLowerCase(); 
        }
        if (typeof y == "string")
        {
            y = (""+y).toLowerCase();
        }

        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });
}

3
Предлагаемое решение может дать ошибку, если a [key] и b [key] не являются обеими строками. Я предлагаю заменить y = y.toLowerCase () на y = ("" + y) .toLowerCase ()
user8074

Сортировка может принять любое положительное или отрицательное число в качестве действительного возврата. Вы дополнительные расчеты, чтобы заставить его быть 1,0, -1 не нужно. Вы прошли сложное простое возвращаемое значение. Лучше не добавлять дополнительные вычисления, которые ничего не делают.
Патрик В. МакМахон

15

Используйте подчеркивание js или lodash,

var arrObj = [
    {
        "updated_at" : "2012-01-01T06:25:24Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-09T11:25:13Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-05T04:13:24Z",
        "foo" : "bar"
    }
];

arrObj = _.sortBy(arrObj,"updated_at");

_.sortBy() возвращает новый массив

см. http://underscorejs.org/#sortBy и документы lodash https://lodash.com/docs#sortBy


6

С поддержкой ES2015 это можно сделать:

foo.sort((a, b) => a.updated_at < b.updated_at ? -1 : 1)

1
нет необходимости для inline, если заменить <на - и удалить '? -1: 1 "вы получите действительное возвращение. В этом примере перемещаются элементы, которые могут быть равны и, следовательно, могут дать неожиданные результаты. Для равных элементов должен быть возвращен 0.
Патрик В. МакМахон

Спасибо за объяснение
ноу-хау

если updated_at - время ISO, это не будет работать. В этом примере предполагается использование меток времени Unix, но OP опубликовал данные в формате ISO. Так что вам придется конвертировать в метки времени Unix, чтобы сделать сравнение. Это можно сделать new Date(iso_str).getTime(), вернув метку времени Unix.
Патрик В. МакМахон

5

Данные импортированы

[
    {
        "gameStatus": "1",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 11:32:04"
    },
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:08:24"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:35:40"
    },
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 10:42:53"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 10:54:09"
    },
    {
        "gameStatus": "0",
        "userId": "1a2fefb0-5ae2-47eb-82ff-d1b2cc27875a",
        "created_at": "2018-12-19 18:46:22"
    },
    {
        "gameStatus": "1",
        "userId": "7118ed61-d8d9-4098-a81b-484158806d21",
        "created_at": "2018-12-20 10:50:48"
    }
]

В порядке возрастания

arr.sort(function(a, b){
    var keyA = new Date(a.updated_at),
        keyB = new Date(b.updated_at);
    // Compare the 2 dates
    if(keyA < keyB) return -1;
    if(keyA > keyB) return 1;
    return 0;
});

Пример заказа Asc

[
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 10:42:53"
    },
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:08:24"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:35:40"
    },
    {
        "gameStatus": "0",
        "userId": "1a2fefb0-5ae2-47eb-82ff-d1b2cc27875a",
        "created_at": "2018-12-19 18:46:22"
    },
    {
        "gameStatus": "1",
        "userId": "7118ed61-d8d9-4098-a81b-484158806d21",
        "created_at": "2018-12-20 10:50:48"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 10:54:09"
    },
    {
        "gameStatus": "1",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 11:32:04"
    }
]

По убыванию

arr.sort(function(a, b){
    var keyA = new Date(a.updated_at),
        keyB = new Date(b.updated_at);
    // Compare the 2 dates
    if(keyA > keyB) return -1;
    if(keyA < keyB) return 1;
    return 0;
});

Пример для Desc Order

[
    {
        "gameStatus": "1",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 11:32:04"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 10:54:09"
    },
    {
        "gameStatus": "1",
        "userId": "7118ed61-d8d9-4098-a81b-484158806d21",
        "created_at": "2018-12-20 10:50:48"
    },
    {
        "gameStatus": "0",
        "userId": "1a2fefb0-5ae2-47eb-82ff-d1b2cc27875a",
        "created_at": "2018-12-19 18:46:22"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:35:40"
    },
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:08:24"
    },
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 10:42:53"
    }
]

3

Как говорится в этом ответе, вы можете использовать Array.sort.

arr.sort(function(a,b){return new Date(a.updated_at) - new Date(b.updated_at)})

arr = [
    {
        "updated_at" : "2012-01-01T06:25:24Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-09T11:25:13Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-05T04:13:24Z",
        "foo" : "bar"
    }
];
arr.sort(function(a,b){return new Date(a.updated_at) - new Date(b.updated_at)});
console.log(arr);


2

Еще один, более математический способ сделать то же самое, но короче :

arr.sort(function(a, b){
    var diff = new Date(a.updated_at) - new Date(b.updated_at);
    return diff/(Math.abs(diff)||1);
});

или в стиле гладкой лямбда-стрелки:

arr.sort((a, b) => {
    var diff = new Date(a.updated_at) - new Date(b.updated_at);
    return diff/(Math.abs(diff)||1);
});

Этот метод может быть сделан с любым числовым вводом


недооцененный ответ
two7s_clash


2

Я создал функцию сортировки в Typescript, которую мы можем использовать для поиска строк, дат и чисел в массиве объектов. Он также может сортировать по нескольким полям.

export type SortType = 'string' | 'number' | 'date';
export type SortingOrder = 'asc' | 'desc';

export interface SortOptions {
  sortByKey: string;
  sortType?: SortType;
  sortingOrder?: SortingOrder;
}


class CustomSorting {
    static sortArrayOfObjects(fields: SortOptions[] = [{sortByKey: 'value', sortType: 'string', sortingOrder: 'desc'}]) {
        return (a, b) => fields
          .map((field) => {
            if (!a[field.sortByKey] || !b[field.sortByKey]) {
              return 0;
            }

            const direction = field.sortingOrder === 'asc' ? 1 : -1;

            let firstValue;
            let secondValue;

            if (field.sortType === 'string') {
              firstValue = a[field.sortByKey].toUpperCase();
              secondValue = b[field.sortByKey].toUpperCase();
            } else if (field.sortType === 'number') {
              firstValue = parseInt(a[field.sortByKey], 10);
              secondValue = parseInt(b[field.sortByKey], 10);
            } else if (field.sortType === 'date') {
              firstValue = new Date(a[field.sortByKey]);
              secondValue = new Date(b[field.sortByKey]);
            }
            return firstValue > secondValue ? direction : firstValue < secondValue ? -(direction) : 0;

          })
          .reduce((pos, neg) => pos ? pos : neg, 0);
      }
    }
}

Использование:

const sortOptions = [{
      sortByKey: 'anyKey',
      sortType: 'string',
      sortingOrder: 'asc',
    }];

arrayOfObjects.sort(CustomSorting.sortArrayOfObjects(sortOptions));

1

Сортировка по дате в формате ISO может быть дорогой, если только вы не ограничиваете клиентов последними и лучшими браузерами, которые могут создать правильную метку времени путем анализа строки Date.

Если вы уверены в своем вводе и знаете, что это всегда будет гггг-мм-ддТчч: мм: сс и GMT (Z), вы можете извлечь цифры из каждого члена и сравнить их как целые числа

array.sort(function(a,b){
    return a.updated_at.replace(/\D+/g,'')-b.updated_at.replace(/\D+/g,'');
});

Если дата может быть отформатирована по-разному, вам может потребоваться добавить что-то для людей с ограниченными возможностями:

Date.fromISO: function(s){
    var day, tz,
    rx=/^(\d{4}\-\d\d\-\d\d([tT ][\d:\.]*)?)([zZ]|([+\-])(\d\d):(\d\d))?$/,
    p= rx.exec(s) || [];
    if(p[1]){
        day= p[1].split(/\D/).map(function(itm){
            return parseInt(itm, 10) || 0;
        });
        day[1]-= 1;
        day= new Date(Date.UTC.apply(Date, day));
        if(!day.getDate()) return NaN;
        if(p[5]){
            tz= (parseInt(p[5], 10)*60);
            if(p[6]) tz+= parseInt(p[6], 10);
            if(p[4]== '+') tz*= -1;
            if(tz) day.setUTCMinutes(day.getUTCMinutes()+ tz);
        }
        return day;
    }
    return NaN;
}
if(!Array.prototype.map){
    Array.prototype.map= function(fun, scope){
        var T= this, L= T.length, A= Array(L), i= 0;
        if(typeof fun== 'function'){
            while(i< L){
                if(i in T){
                    A[i]= fun.call(scope, T[i], i, T);
                }
                ++i;
            }
            return A;
        }
    }
}
}

2
Не могли бы вы просто использовать Date.parse?
Ракета Хазмат

1

Для полноты здесь возможна краткая обобщенная реализация sortBy:

function sortBy(list, keyFunc) {
  return list.sort((a,b) => keyFunc(a) - keyFunc(b));
}

sortBy([{"key": 2}, {"key": 1}], o => o["key"])

Обратите внимание, что здесь используется метод сортировки массивов, который сортирует по месту. для копирования вы можете использовать arr.concat () или arr.slice (0) или аналогичный метод для создания копии.


1

При этом мы можем передать ключевую функцию для использования для сортировки

Array.prototype.sortBy = function(key_func, reverse=false){
    return this.sort( (a, b) => {
        var keyA = key_func(a),
            keyB = key_func(b);
        if(keyA < keyB) return reverse? 1: -1;
        if(keyA > keyB) return reverse? -1: 1;
        return 0;
    }); 
}

Тогда, например, если у нас есть

var arr = [ {date: "01/12/00", balls: {red: "a8",  blue: 10}},
            {date: "12/13/05", balls: {red: "d6" , blue: 11}},
            {date: "03/02/04", balls: {red: "c4" , blue: 15}} ]

Мы можем сделать

arr.sortBy(el => el.balls.red)
/* would result in
[ {date: "01/12/00", balls: {red: "a8", blue: 10}},
  {date: "03/02/04", balls: {red: "c4", blue: 15}},
  {date: "12/13/05", balls: {red: "d6", blue: 11}} ]
*/

или

arr.sortBy(el => new Date(el.date), true)   // second argument to reverse it
/* would result in
[ {date: "12/13/05", balls: {red: "d6", blue:11}},
  {date: "03/02/04", balls: {red: "c4", blue:15}},
  {date: "01/12/00", balls: {red: "a8", blue:10}} ]
*/

или

arr.sortBy(el => el.balls.blue + parseInt(el.balls.red[1]))
/* would result in
[ {date: "12/13/05", balls: {red: "d6", blue:11}},    // red + blue= 17
  {date: "01/12/00", balls: {red: "a8", blue:10}},    // red + blue= 18
  {date: "03/02/04", balls: {red: "c4", blue:15}} ]   // red + blue= 19
*/

1

Вы можете использовать служебную библиотеку Lodash для решения этой проблемы (это довольно эффективная библиотека):

const data = [{
    "updated_at": "2012-01-01T06:25:24Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-09T11:25:13Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-05T04:13:24Z",
    "foo": "bar"
  }
]

const ordered = _.orderBy(
  data,
  function(item) {
    return item.updated_at;
  }
);

console.log(ordered)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

Вы можете найти документацию здесь: https://lodash.com/docs/4.17.15#orderBy


0

Вы можете создать замыкание и передать его таким образом, вот мой пример работы

$.get('https://data.seattle.gov/resource/3k2p-39jp.json?$limit=10&$where=within_circle(incident_location, 47.594972, -122.331518, 1609.34)', 
  function(responce) {

    var filter = 'event_clearance_group', //sort by key group name
    data = responce; 

    var compare = function (filter) {
        return function (a,b) {
            var a = a[filter],
                b = b[filter];

            if (a < b) {
                return -1;
            } else if (a > b) {
                return 1;
            } else {
                return 0;
            }
        };
    };

    filter = compare(filter); //set filter

    console.log(data.sort(filter));
});

0
var months = [
    {
        "updated_at" : "2012-01-01T06:25:24Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-09T11:25:13Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-05T04:13:24Z",
        "foo" : "bar"
    }];
months.sort((a, b)=>{
    var keyA = new Date(a.updated_at),
        keyB = new Date(b.updated_at);
    // Compare the 2 dates
    if(keyA < keyB) return -1;
    if(keyA > keyB) return 1;
    return 0;
});
console.log(months);

0
  • использование Array.sort() для сортировки массива
  • Клонируйте массив с помощью оператора распространения ( ), чтобы сделать функцию чистой
  • Сортировать по нужному ключу ( updated_at)
  • Преобразовать строку даты в объект даты
  • Array.sort() работает, вычитая два свойства из текущего и следующего элемента, если это число / объект, над которым вы можете выполнять аритмические операции
const input = [
  {
    updated_at: '2012-01-01T06:25:24Z',
    foo: 'bar',
  },
  {
    updated_at: '2012-01-09T11:25:13Z',
    foo: 'bar',
  },
  {
    updated_at: '2012-01-05T04:13:24Z',
    foo: 'bar',
  }
];

const sortByUpdatedAt = (items) => [...items].sort((itemA, itemB) => new Date(itemA.updated_at) - new Date(itemB.updated_at));

const output = sortByUpdatedAt(input);

console.log(input);
/*
[ { updated_at: '2012-01-01T06:25:24Z', foo: 'bar' }, 
  { updated_at: '2012-01-09T11:25:13Z', foo: 'bar' }, 
  { updated_at: '2012-01-05T04:13:24Z', foo: 'bar' } ]
*/
console.log(output)
/*
[ { updated_at: '2012-01-01T06:25:24Z', foo: 'bar' }, 
  { updated_at: '2012-01-05T04:13:24Z', foo: 'bar' }, 
  { updated_at: '2012-01-09T11:25:13Z', foo: 'bar' } ]
*/

0

Я сталкиваюсь с тем же, так что я справляюсь с общим и почему я создаю функцию для этого:

//example:
//array: [{name: 'idan', workerType: '3'}, {name: 'stas', workerType: '5'}, {name: 'kirill', workerType: '2'}]
//keyField: 'workerType'
// keysArray: ['4', '3', '2', '5', '6']
sortByArrayOfKeys = (array, keyField, keysArray) => {
    array.sort((a, b) => {
        const aIndex = keysArray.indexOf(a[keyField])
        const bIndex = keysArray.indexOf(b[keyField])
        if (aIndex < bIndex) return -1;
        if (aIndex > bIndex) return 1;
        return 0;
    })
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.