Sortuj tablicę obiektów według pojedynczego klucza z wartością daty


257

Mam tablicę obiektów z kilkoma parami kluczowych wartości i muszę je posortować na podstawie „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"
    }
]

Jaki jest najbardziej efektywny sposób?


@Topener Ten link wygląda na pytanie o PHP
David Brainer,

mój błąd ... nie przeczytałem go poprawnie
Rene Pot

Odpowiedzi:


337

Możesz użyć Array.sort.

Oto przykład:

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
Nie możesz użyć keyA - keyB(lub ewentualnie keyB - keyA)? Obiekty daty mają valueOf()metodę.
soktinpk

zwrócić a. updated_at <b. aktualizacja_at? 1: -1 działa dla mnie. Nie trzeba analizować formatu daty.
oliversisson

Możliwość zrobienia tego a-bjest naprawdę ważna, jeśli chcesz uniknąć długiego kodu. Podczas gdy długi kod niekoniecznie jest zły w tym przypadku, myślę, że gadatliwość utrudnia zrozumienie. Obecnie używam values.sort((a,b)=>a.attr-b.attr). Konieczność pisania 5 wierszy za każdym razem, gdy trzeba posortować tablicę, staje się nużąca.
AnnanFay

Ten przykład nie jest skuteczny i wykonuje wiele niepotrzebnych obliczeń. Sortowanie może przyjmować dowolną liczbę dodatnią lub ujemną jako prawidłowy wynik. Twoje dodatkowe obliczenia, aby zmusić go do wartości 1,0, -1, nie są potrzebne i po prostu dodają dodatkowe obliczenia na wykonanie.
Patrick W. McMahon,

Działa bardzo dziękuję
Andy

158

Odpowiedziałem już na bardzo podobne pytanie: prosta funkcja sortowania tablicy obiektów

Dla tego pytania stworzyłem tę małą funkcję, która może robić, co chcesz:

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


2
Jak byś to odwrócił?

4
Aby nie rozróżniać wielkości liter, można dodać .toLowerCase () do zmiennych x i y
Jacob van Lingen

4
Aby odwrócić takie funkcje sortowania, wystarczy pomnożyć wynik przez -1:)
Svish

5
Lub po prostu weź wynik i użyj array = array.reverse()funkcji.
Luke Stevenson

31

Metoda Array.sort () sortuje elementy tablicy na miejscu i zwraca tablicę. Uważaj na Array.sort (), ponieważ nie jest ona niezmienna . Do sortowania niezmiennego użyj sortowania niezmiennego .

Ta metoda polega na sortowaniu tablicy przy użyciu prądu updated_atw formacie ISO. Używamynew Data(iso_string).getTime() do konwersji czasu ISO na znacznik czasu Unix. Uniksowy znacznik czasu to liczba, na której możemy wykonywać proste obliczenia matematyczne. Odejmujemy pierwszy i drugi znacznik czasu, wynik jest; jeśli pierwszy znacznik czasu jest większy niż drugi, zwracana liczba będzie dodatnia. Jeśli druga liczba jest większa od pierwszej, zwracana wartość będzie ujemna. Jeśli dwa są takie same, zwrot wyniesie zero. To idealnie pasuje do wymaganych wartości zwracanych dla funkcji inline.

W przypadku ES6 :

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

W przypadku ES5 :

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

Jeśli zmienisz swój updated_at na uniksowe znaczniki czasu, możesz to zrobić:

W przypadku ES6 :

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

Dla ES5 :

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

W chwili pisania tego postu nowoczesne przeglądarki nie obsługują ES6. Aby używać ES6 we współczesnych przeglądarkach, użyj babel do transponowania kodu do ES5. W najbliższej przyszłości spodziewaj się obsługi przeglądarki ES6.

Array.sort () powinien otrzymać wartość zwrotną jednego z 3 możliwych wyników:

  • Liczba dodatnia (pierwszy przedmiot> drugi przedmiot)
  • Liczba ujemna (pierwszy element <drugi element)
  • 0, jeśli dwa elementy są równe

Zauważ, że zwracana wartość funkcji inline może być dowolną liczbą dodatnią lub ujemną. Array.Sort () nie obchodzi, jaki jest numer zwracany. Dba o to tylko, jeśli zwracana wartość jest dodatnia, ujemna lub zero.

W przypadku sortowania niezmiennego: (przykład w ES6)

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

Możesz także napisać w ten sposób:

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

Zaimportowany plik to nowy sposób na włączenie javascript do ES6 i sprawia, że ​​kod wygląda bardzo czysto. Mój osobisty faworyt.

Niezmienne sortowanie nie powoduje mutacji tablicy źródłowej, a zwraca nową tablicę. Używanie constjest zalecane w przypadku danych niezmiennych.


19

Oto nieco zmodyfikowana wersja odpowiedzi @David Brainer-Bankers, która sortuje alfabetycznie według łańcucha lub liczbowo według liczby i zapewnia, że ​​słowa zaczynające się od wielkich liter nie będą sortowane powyżej słów zaczynających się na małe litery (np. „Jabłko, wczesne” będą wyświetlane w tej kolejności).

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
Proponowane rozwiązanie może dawać błąd, jeśli [klucz] ib [klucz] nie są ciągami. Sugeruję zastąpienie y = y.toLowerCase () na y = ("" + y) .toLowerCase ()
8074

sort może przyjąć dowolną liczbę dodatnią lub ujemną jako prawidłowy zwrot. Masz dodatkowe obliczenia, aby zmusić go do wartości 1,0, -1 nie jest potrzebne. Nadmiernie skomplikowałeś prostą wartość zwrotu. Najlepiej nie dodawać dodatkowych obliczeń, które nic nie robią.
Patrick W. McMahon,


6

Dzięki obsłudze ES2015 można to zrobić poprzez:

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

1
nie ma potrzeby wstawiania, jeśli zamień <na - i usuń '? -1: 1 "otrzymasz prawidłowy zwrot. Ten przykład przenosi przedmioty, które mogą być równe, a zatem mogą dawać nieoczekiwane wyniki. Dla równych przedmiotów należy zwrócić wartość 0.
Patrick W. McMahon

Dzięki za wyjaśnienie
zna

jeśli aktualizacja_at jest czasem ISO, to nie zadziała. W tym przykładzie przyjęto uniksowe znaczniki czasu, ale OP opublikował dane w formacie ISO. Musisz więc przekonwertować na uniksowe znaczniki czasu, aby dokonać porównania. Można to zrobić w new Date(iso_str).getTime()ten sposób zwróci znacznik czasu Unix.
Patrick W. McMahon,

5

Dane zaimportowane

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

W porządku rosnącym

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

Przykład zamówienia 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"
    }
]

W porządku malejącym

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

Przykład zamówienia opisu

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

Jak stwierdza ta odpowiedź, możesz użyć 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

Kolejny, bardziej matematyczny sposób robienia tego samego, ale krótszy :

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

lub w zgrabnym stylu strzały lambda:

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

Metodę tę można wykonać przy użyciu dowolnego wprowadzania numerycznego


niedostateczna odpowiedź
two7s_clash


2

Stworzyłem funkcję sortowania w Typescript, której możemy używać do wyszukiwania ciągów, dat i liczb w tablicy obiektów. Może także sortować według wielu pól.

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

Stosowanie:

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

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

1

Sortowanie według daty sformatowanej według ISO może być kosztowne, chyba że ograniczysz klientów do najnowszych i najlepszych przeglądarek, które mogą utworzyć prawidłowy znacznik czasu, analizując ciąg znaków.

Jeśli jesteś pewien swoich danych wejściowych i wiesz, że zawsze będzie to rrrr-mm-ddThh: mm: ss i GMT (Z), możesz wyodrębnić cyfry z każdego elementu i porównać je jak liczby całkowite

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

Jeśli data może być sformatowana inaczej, może być konieczne dodanie czegoś dla osób z iso-wyzwanie:

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
Nie możesz po prostu użyć Date.parse?
Rocket Hazmat

1

Dla kompletności tutaj jest możliwe krótkie ogólne wdrożenie sortBy:

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

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

Zauważ, że używa to metody sortowania tablic, która sortuje się w miejscu. dla kopii możesz użyć arr.concat () lub arr.slice (0) lub podobnej metody, aby utworzyć kopię.


1

Dzięki temu możemy przekazać kluczową funkcję do użycia przy sortowaniu

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

Na przykład, jeśli mamy

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

Możemy zrobić

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}} ]
*/

lub

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}} ]
*/

lub

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

Możesz użyć biblioteki narzędzi Lodash , aby rozwiązać ten problem (jest to dość wydajna biblioteka):

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>

Dokumentację można znaleźć tutaj: https://lodash.com/docs/4.17.15#orderBy


0

Możesz stworzyć zamknięcie i przekazać to w ten sposób , działa mój przykład

$.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
  • Służy Array.sort()do sortowania tablicy
  • Klonuj tablicę za pomocą operatora spread ( ), aby funkcja była czysta
  • Sortuj według żądanego klucza ( updated_at)
  • Konwertuj ciąg daty na obiekt daty
  • Array.sort() działa poprzez odjęcie dwóch właściwości od bieżącego i następnego elementu, jeśli jest to liczba / obiekt, na którym można wykonywać operacje arytmiczne
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

Mam do czynienia z tą samą rzeczą, więc radzę sobie z tym ogólną przyczyną i buduję dla tego funkcję:

//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;
    })
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.