Pytanie Sortuj tablicę obiektów według wartości właściwości łańcuchowej w JavaScript


Mam tablicę obiektów JavaScript:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

Jak mogę je posortować według wartości last_nom w JavaScript?

wiem o sort(a,b), ale to tylko wydaje się działać na ciągi i liczby. Czy muszę dodać metodę toString do moich obiektów?


1829
2017-07-15 03:17


pochodzenie


Ten skrypt pozwala ci to zrobić, chyba że chcesz napisać własną funkcję porównania lub sorter: thomasfrank.se/sorting_things.html - Baversjo


Odpowiedzi:


Łatwo jest napisać własną funkcję porównania:

function compare(a,b) {
  if (a.last_nom < b.last_nom)
    return -1;
  if (a.last_nom > b.last_nom)
    return 1;
  return 0;
}

objs.sort(compare);

Lub inline (c / o Marco Demaio):

objs.sort(function(a,b) {return (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0);} ); 

2716
2017-07-15 03:35



Lub inline: objs.sort (function (a, b) {return (a.last_nom> b.last_nom)? 1: ((b.last_nom> a.last_nom)? -1: 0);}); - Marco Demaio
Oficjalne dokumenty: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/... - mikemaccana
return a.last_nom.localeCompare(b.last_nom) też będzie działać. - Cerbrus
dla osób szukających sortu, w którym pole jest numeryczne, ciało funkcji porównania: return a.value - b.value; (ASC) - Andre Figueiredo
@Cerbrus localeCompare jest ważny, gdy używasz znaków akcentowanych w językach obcych, a także bardziej eleganckiej. - Marcos Lima


Możesz również utworzyć funkcję sortowania dynamicznego, która sortuje obiekty według ich wartości, którą przekazujesz:

function dynamicSort(property) {
    var sortOrder = 1;
    if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a,b) {
        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result * sortOrder;
    }
}

Więc możesz mieć tablicę takich obiektów:

var People = [
    {Name: "Name", Surname: "Surname"},
    {Name:"AAA", Surname:"ZZZ"},
    {Name: "Name", Surname: "AAA"}
];

... i zadziała, kiedy to zrobisz:

People.sort(dynamicSort("Name"));
People.sort(dynamicSort("Surname"));
People.sort(dynamicSort("-Surname"));

Właściwie to już odpowiada na pytanie. Poniżej część jest napisana, ponieważ wiele osób skontaktowało się ze mną, narzekając na to nie działa z wieloma parametrami.

Wiele parametrów

Możesz użyć poniższej funkcji do wygenerowania funkcji sortowania z wieloma parametrami sortowania.

function dynamicSortMultiple() {
    /*
     * save the arguments object as it will be overwritten
     * note that arguments object is an array-like object
     * consisting of the names of the properties to sort by
     */
    var props = arguments;
    return function (obj1, obj2) {
        var i = 0, result = 0, numberOfProperties = props.length;
        /* try getting a different result from 0 (equal)
         * as long as we have extra properties to compare
         */
        while(result === 0 && i < numberOfProperties) {
            result = dynamicSort(props[i])(obj1, obj2);
            i++;
        }
        return result;
    }
}

Które umożliwiłoby zrobienie czegoś takiego:

People.sort(dynamicSortMultiple("Name", "-Surname"));

Dodawanie do prototypu

(Implementacja, która jest tuż poniżej, jest inspirowana Mike R„s odpowiedź)

Nie polecałbym zmiany prototypu natywnego obiektu, ale po prostu dawałem przykład, który można zaimplementować na własnych obiektach (W przypadku środowisk, które ją obsługują, możesz również użyć Object.defineProperty jak pokazano w następnym rozdziale, który przynajmniej nie ma negatywnego efektu ubocznego bycia przeliczalnym, jak opisano w ostatniej części)

Implementacja prototypu byłaby podobna do następującej (Oto działający przykład):

//Don't just copy-paste this code. You will break the "for-in" loops
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Array.prototype.sortBy = function() {
        return this.sort(_dynamicSortMultiple.apply(null, arguments));
    }
}();

Sposób "OK" dodawania go do prototypu

Jeśli kierujesz reklamy na IE w wersji 9.0, a potem, jak już wspomniałem, użyj Object.defineProperty lubię to (przykład pracy):

//Won't work below IE9, but totally safe otherwise
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Object.defineProperty(Array.prototype, "sortBy", {
        enumerable: false,
        writable: true,
        value: function() {
            return this.sort(_dynamicSortMultiple.apply(null, arguments));
        }
    });
}();

Może to być dopuszczalny kompromis do czasu bind operator przybywa.

Ta prototypowa zabawa umożliwia:

People.sortBy("Name", "-Surname");

Powinieneś to przeczytać

Jeśli używasz metody bezpośredniego dostępu prototypowego (Object.defineProperty jest w porządku), a inny kod nie sprawdza hasOwnProperty, kocięta umierają! Ok, szczerze mówiąc, żadna szkoda nie przynosi uszczerbku, ale prawdopodobnie wszystko się zepsuje i każdy inny programista w twoim zespole będzie cię nienawidził:

evil

Zobacz ostatnią "SortBy"? Tak. Nie fajnie. Użyj Object.defineProperty, gdzie możesz i zostaw osobno prototyp Array.prototype.


650
2018-01-21 15:03



Należy pamiętać, że nazwy właściwości w JavaScript mogą być dowolnym ciągiem znaków i jeśli masz właściwości zaczynające się od "-" (bardzo mało prawdopodobne i prawdopodobnie nie jest to dobry pomysł), musisz zmodyfikować funkcję dynamicSort, aby użyć czegoś innego jako wskaźnika sortowania wstecznego. - Ege Özcan
Zauważyłem, że dynamicSort() w powyższym przykładzie umieszcza wielkie litery przed małymi literami. Na przykład, jeśli mam wartości APd, Aklin, i Abe - wyniki w sortowaniu ASC powinny być Abe, Aklin, APd. Ale z twoim przykładem, wyniki są APd, Abe, Aklin. W każdym razie, aby poprawić to zachowanie? - Lloyd Banks
@LloydBanks, jeśli używasz tego wyłącznie do ciągów, możesz użyć var result = a[property].localeCompare(b[property]); zamiast var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;. - Ege Özcan
Tak jak najwyższa odpowiedź, ta nie powiedzie się w przypadkach, gdy istnieją nieokreślone właściwości: [ { a: 5 }, {a:2}, {}, {a:1} ] - dzh
@ EgeÖzcan Powinien albo umieścić elementy na górze, albo na dole, tutaj jest implementacja, której używałem pastebin.com/g4dt23jU - dzh


underscore.js

użyj podkreślenia, jego małego i niesamowitego ...

sortBy_.sortBy (list, iterator, [context]) Zwraca posortowaną kopię   lista, uszeregowana w porządku rosnącym według wyników wykonywania każdej wartości   przez iterator. Iterator może być również nazwą ciągu właściwości   sortować według (np. długość).

var objs = [ 
  { first_nom: 'Lazslo',last_nom: 'Jamf' },
  { first_nom: 'Pig', last_nom: 'Bodine'  },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];

var sortedObjs = _.sortBy( objs, 'first_nom' );

155
2018-05-10 21:24



David, czy mógłbyś edytować odpowiedź, mówiąc: var sortedObjs = _.sortBy( objs, 'first_nom' );. objs będzie nie w wyniku tego posortuj się. Funkcja będzie powrót posortowana tablica. To by uczyniło to bardziej wyraźnym. - Jess
Aby odwrócić sortowanie: var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse(); - Erdal G.
musisz wczytać bibliotekę javascript "podkreślenie": <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"> </script> - and-bri
Dostępne również w Lodash dla tych, którzy wolą ten - WoJ


Nie rozumiem, dlaczego ludzie sprawiają, że jest to tak skomplikowane:

objs.sort(function(a, b){
  return a.last_nom > b.last_nom;
});

Dla bardziej rygorystycznych silników:

objs.sort(function(a, b){
  return a.last_nom == b.last_nom ? 0 : +(a.last_nom > b.last_nom) || -1;
});

Zamień operatora, aby posortował go według odwrotnej kolejności alfabetycznej.


141
2018-01-24 19:35



To nie jest poprawne, ponieważ funkcja używana w sortowaniu powinna zwracać wartość -1, 0 lub 1, ale powyższa funkcja zwraca wartość logiczną. Sortowanie działa dobrze w chrome, ale kończy się niepowodzeniem w PhantomJS na przykład. Widzieć code.google.com/p/phantomjs/issues/detail?id=1090 - schup
Niektóre silniki stanowią głupotę, w ten sposób był to rodzaj wykorzystania. Zaktualizowałem swoją odpowiedź odpowiednią wersją. - p3lim
Sugeruję edycję, aby wykupić pierwszą wersję. Jest bardziej zwięzły, więc wygląda bardziej atrakcyjnie, ale nie działa, a przynajmniej nie jest niezawodny. Jeśli ktoś spróbuje go w jednej przeglądarce i zadziała, może nawet nie zdawać sobie sprawy, że ma problem (szczególnie, jeśli nie przeczytał komentarzy). Druga wersja działa poprawnie, więc naprawdę nie ma potrzeby na pierwszą. - Kate
Czy istnieje wersja pierwszej z wieloma parametrami - szczególnie patrząc na sortowanie według liczebności, a następnie alfabetycznie - Lion789
@Simon Naprawdę doceniam, że najpierw mam "nieco złą" wersję, ponieważ zaostrzenie implementacji zajmuje kilka sekund, aby parsować i zrozumieć, a bez tego byłoby znacznie trudniej. - Aaron Sherman


W ES6 / ES2015 lub nowszym możesz zrobić w ten sposób:

objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom));

138
2018-01-29 19:44



jest to dostępne od JS 1.1, kawałek grubej strzałki do tego jest kawałkiem ES6 / 2015. Ale nadal bardzo przydatne i najlepszą odpowiedź w mojej opinii - Jon Harding
@PratikKelwalkar: jeśli potrzebujesz odwrócić to po prostu zmień porównanie aib: objs.sort ((a, b) => b.last_nom.localeCompare (a.last_nom)); - Vlad Bezden
czy możliwe jest użycie indeksu do adresowania pola do sortowania: zamiast last_nom użyj tylko liczby w tablicy: 1 ? - and-bri
@VladBezden dzięki za odpowiedź! To rozwiązanie jest pierwszym z bardzo małym wysiłkiem programistycznym i prawidłowymi wynikami sortowania oraz tablicą ciągów, jak: ["Nazwa1", "Nazwa10", "Nazwa2", "coś innego", "Nazwa11"]. Z sortowaniem działam poprawnie objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom, undefined, {numberic: true})); - scipper


Jeśli masz zduplikowane nazwiska, możesz je posortować według imienia

obj.sort(function(a,b){
  if(a.last_nom< b.last_nom) return -1;
  if(a.last_nom >b.last_nom) return 1;
  if(a.first_nom< b.first_nom) return -1;
  if(a.first_nom >b.first_nom) return 1;
  return 0;
});

51
2017-07-15 04:03



Że a< b  a >b formatowanie jest interesujące. - Dodekeract


Proste i szybkie rozwiązanie tego problemu z wykorzystaniem prototypowego dziedziczenia:

Array.prototype.sortBy = function(p) {
  return this.slice(0).sort(function(a,b) {
    return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;
  });
}

Przykład / użycie

objs = [{age:44,name:'vinay'},{age:24,name:'deepak'},{age:74,name:'suresh'}];

objs.sortBy('age');
// Returns
// [{"age":24,"name":"deepak"},{"age":44,"name":"vinay"},{"age":74,"name":"suresh"}]

objs.sortBy('name');
// Returns
// [{"age":24,"name":"deepak"},{"age":74,"name":"suresh"},{"age":44,"name":"vinay"}]

Aktualizacja: Nie modyfikuje już oryginalnej tablicy.


38
2017-07-10 11:54



Nie wystarczy zwrócić kolejną tablicę. ale faktycznie sortuje oryginał !. - Vinay Aggarwal
Jeśli chcesz się upewnić, że używasz sortowania naturalnego z liczbami (tj. 0,1,2,10,11 itd.), Użyj parseInt z zestawem Radix. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... so: return (parseInt (a [p], 10)> parseInt (b [p], 10))? 1: (parseInt (a [p], 10) <parseInt (b [p], 10))? -1: 0; - Paul
@codehuntr Dzięki za poprawienie. ale myślę, że zamiast funkcji sortowania zrobić sensytyzację, lepiej, jeśli zrobimy oddzielną funkcję, aby naprawić typy danych. Ponieważ funkcja sort nie może stwierdzić, która właściwość będzie zawierać dane rodzaju. :) - Vinay Aggarwal
Bardzo dobrze. Odwróć strzałki, aby uzyskać efekt asc / desc. - Abdul Sadik Yalcin