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

У меня есть массив объектов с несколькими парами ключ-значение, и мне нужно отсортировать их на основе "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"
    }
]

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

22 ответа

Решение

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

Вот (непроверенный) пример:

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;
});

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

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

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));
    });
}

Метод 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 рекомендуется на неизменных данных.

Используйте подчеркивание 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/ и документы lodash https://lodash.com/docs

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

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

Вот слегка измененная версия ответа @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));
    });
}

Я немного опоздал, но в 2021 году правильный ответ — использовать. updated_atявляется строкой ISO-8601 и, следовательно, может сортироваться как строка. Преобразование в Dateэто пустая трата времени и так работает ifсравнения вручную, чтобы вернуть 0, 1 или -1.

      const 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"
  }
];

const { compare } = Intl.Collator('en-US');
arr.sort((a, b) => compare(a.updated_at, b.updated_at));

Intl.Collatorвозвращает функцию, которую можно использовать как compareFunctionза #Array.sort. Поскольку мы сортируем объект, мы вызываем compareсо значениями ключа, по которому мы хотим отсортировать.

Обратите внимание, что если бы мы сортировали массив строк, мы могли бы просто сделать это:

      arr.sort(compare);

Также обратите внимание, что, как указывали другие, sortизменит исходный массив. Если это нежелательно, вы можете сначала клонировать его. В 2021 году это можно сделать следующим образом:

      [...arr].sort((a, b) => compare(a.updated_at, b.updated_at));

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

[
    {
        "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"
    }
]

На сегодняшний день ответы @knowbody ( /questions/36754908/sortirovat-massiv-obektov-po-odnomu-klyuchu-so-znacheniem-datyi/36754924#36754924) и @Rocket Hazmat ( /questions/36754908/sortirovat-massiv-obektov-po-odnomu-klyuchu-so-znacheniem-datyi/36754928#36754928) можно объединить для обеспечения поддержки ES2015 и правильная обработка даты:

arr.sort((a, b) => {
   const dateA = new Date(a.updated_at);
   const dateB = new Date(b.updated_at);
   return dateA - dateB;
});

Как говорится в этом ответе, вы можете использовать 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);

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

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);
});

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

Вы можете использовать служебную библиотеку 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

Array.prototype.sortBy = function(key_func, reverse=false){
    return this.sort( (a, b) => ( key_func(b) - key_func(a) ) * (reverse ? 1 : -1) ) 
}

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

var arr = [ {id: 0, balls: {red: 8,  blue: 10}},
            {id: 2, balls: {red: 6 , blue: 11}},
            {id: 1, balls: {red: 4 , blue: 15}} ]

arr.sortBy(el => el.id, reverse=true)
/* would result in
[ { id: 2, balls: {red: 6 , blue: 11 }},
  { id: 1, balls: {red: 4 , blue: 15 }},
  { id: 0, balls: {red: 8 , blue: 10 }} ]
*/

или же

arr.sortBy(el => el.balls.red + el.balls.blue)
/* would result in
[ { id: 2, balls: {red: 6 , blue: 11 }},    // red + blue= 17
  { id: 0, balls: {red: 8 , blue: 10 }},    // red + blue= 18
  { id: 1, balls: {red: 4 , blue: 15 }} ]   // red + blue= 19
*/

Я создал сортировочную функцию в 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));

Для полноты здесь возможна краткая обобщенная реализация 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) или аналогичный метод для создания копии.

Сортировка по дате в формате 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;
        }
    }
}
}
  • использование 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' } ]
*/

Простое правильное решение:

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

//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;
    })
}
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);

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

$.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));
});
Другие вопросы по тегам