Domanda Ordina array di oggetti per valore di proprietà stringa in JavaScript


Ho una matrice di oggetti JavaScript:

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

Come posso ordinarli per il valore di last_nom in JavaScript?

Lo so sort(a,b), ma sembra funzionare solo su stringhe e numeri. Devo aggiungere un metodo toString ai miei oggetti?


1815
2017-07-15 03:17


origine


risposte:


È abbastanza facile scrivere la tua funzione di confronto:

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

O in linea (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);} ); 

2695
2017-07-15 03:35



Puoi anche creare una funzione di ordinamento dinamico che ordina gli oggetti in base al loro valore che hai passato:

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

Quindi puoi avere una serie di oggetti come questo:

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

... e funzionerà quando lo fai:

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

In realtà questo già risponde alla domanda. Qui sotto parte è scritta perché molte persone mi hanno contattato, lamentandomi non funziona con più parametri.

Parametri multipli

È possibile utilizzare la funzione seguente per generare funzioni di ordinamento con più parametri di ordinamento.

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

Che ti consentirebbe di fare qualcosa del genere:

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

Aggiungendolo al prototipo

(L'implementazione che è appena sotto è ispirata da Mike R'S risposta)

Non consiglierei di cambiare un prototipo di un oggetto nativo, ma solo di dare un esempio in modo da poterlo implementare sui propri oggetti (Per gli ambienti che lo supportano, puoi anche usarlo Object.defineProperty come mostrato nella prossima sezione, che almeno non ha l'effetto collaterale negativo di essere enumerabile, come descritto nell'ultima parte)

L'implementazione del prototipo sarebbe simile al seguente (Ecco un esempio funzionante):

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

Il modo "OK" di aggiungerlo al prototipo

Se scegli come target IE v9.0 e versioni successive, come di consueto, usa Object.defineProperty come questo (esempio di lavoro):

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

Questo può essere un compromesso accettabile fino al operatore legante arriva.

Tutto questo divertimento prototipo consente questo:

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

Dovresti leggere questo

Se si utilizza il metodo di accesso al prototipo diretto (Object.defineProperty va bene) e l'altro codice non controlla hasOwnPropertyi gattini muoiono! Ok, ad essere onesti, nessun gattino arriverà davvero, ma probabilmente le cose si romperanno e ogni altro sviluppatore della tua squadra ti odierà:

evil

Vedi quest'ultimo "SortBy"? Si. Non fantastico Utilizzare Object.defineProperty dove è possibile e lasciare il solo Array.prototype.


648
2018-01-21 15:03



underscore.js

usa il trattino basso, è piccolo e fantastico ...

sortBy_.sortBy (lista, iteratore, [contesto]) Restituisce una copia ordinata di   elenco, classificato in ordine crescente in base ai risultati dell'esecuzione di ciascun valore   tramite iteratore. Iterator potrebbe anche essere il nome della stringa della proprietà   per ordinare per (ad esempio la lunghezza).

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



Non capisco perché la gente lo rende così complicato:

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

Per motori più stretti:

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

Scambia l'operatore per ordinarlo in ordine alfabetico inverso.


140
2018-01-24 19:35



In ES6 / ES2015 o versioni successive puoi procedere in questo modo:

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

136
2018-01-29 19:44



Se hai cognomi duplicati potresti ordinarli per nome-

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



Soluzione semplice e rapida a questo problema utilizzando l'ereditarietà del prototipo:

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

Esempio / utilizzo

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

Aggiornare: Non modifica più la matrice originale.


38
2017-07-10 11:54



Invece di utilizzare una funzione di confronto personalizzata, è possibile anche creare un tipo di oggetto con personalizzato toString() metodo (che viene invocato dalla funzione di confronto predefinita):

function Person(firstName, lastName) {
    this.firtName = firstName;
    this.lastName = lastName;
}

Person.prototype.toString = function() {
    return this.lastName + ', ' + this.firstName;
}

var persons = [ new Person('Lazslo', 'Jamf'), ...]
persons.sort();

23
2017-07-15 07:21



Ci sono molte buone risposte qui, ma vorrei sottolineare che possono essere estese molto semplicemente per ottenere un ordinamento molto più complesso. L'unica cosa che devi fare è usare l'operatore OR per concatenare funzioni di comparazione come questa:

objs.sort((a,b)=> fn1(a,b) || fn2(a,b) || fn3(a,b) )

Dove fn1, fn2, ... sono le funzioni di ordinamento che restituiscono [-1,0,1]. Ciò si traduce in "ordinamento di fn1", "ordinamento di fn2" che è praticamente uguale a ORDER BY in SQL.

Questa soluzione è basata sul comportamento di || operatore che valuta il prima espressione valutata che può essere convertita in vera.

La forma più semplice ha solo una funzione inline come questa:

// ORDER BY last_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) )

Avere due passi con last_nom,first_nom l'ordinamento sarebbe simile a questo:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) || 
                  a.first_nom.localeCompare(b.first_nom)  )

Una funzione di comparazione generica potrebbe essere qualcosa di simile a questo:

// ORDER BY <n>
let cmp = (a,b,n)=>a[n].localeCompare(b[n])

Questa funzione può essere estesa per supportare campi numerici, case sensitive, tipi di dati arbitary, ecc.

Puoi usarli con il concatenarli in base alla priorità di ordinamento:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> cmp(a,b, "last_nom") || cmp(a,b, "first_nom") )
// ORDER_BY last_nom, first_nom DESC
objs.sort((a,b)=> cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )
// ORDER_BY last_nom DESC, first_nom DESC
objs.sort((a,b)=> -cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )

Il punto qui è che il puro JavaScript con approccio funzionale può portarvi molto tempo senza librerie esterne o codice complesso. È anche molto efficace, dal momento che non è necessario eseguire l'analisi delle stringhe


14
2018-05-05 11:36