Javascript ordina l'array per due campi


92
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);      
    return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;
});

Quindi il codice sopra ordina l'array per gsize - dal più piccolo al più grande. Funziona bene. Ma se la dimensione g è la stessa, vorrei che fosse ordinata per bagliore.

Grazie.


la funzione sort reagisce al risultato positivo, negativo o zero. quindi puoi semplicemente scrivere: "return aSize - bSize". sarà un codice più semplice e leggibile.

Risposte:


112
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);

    if(aSize == bSize)
    {
        return (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0;
    }
    else
    {
        return (aSize < bSize) ? -1 : 1;
    }
});

Per rendere questo più concisa utilizzando la sintassi freccia, senza ricorrere a tecniche non evidenti mostrati in alcune altre risposte: grouperArray.sort((a, b) => a.gsize == b.gsize ? a.glow - b.glow : a.gsize - b.gsize. Per rendere il codice più facile da capire, manteniamo esplicito il test per ==.
ToolmakerSteve

171
grouperArray.sort(function (a, b) {   
    return a.gsize - b.gsize || a.glow - b.glow;
});

versione più corta


grande scorciatoia! mi ha aiutato a mettere insieme una soluzione più complessa .. stackoverflow.com/questions/6101475/…
Joseph Poirier,

3
Bello e pulito! L'unica cosa che funziona solo per i numeri.
Afanasii Kurakin

Puoi spiegare la logica qui?!. Ha funzionato per me sort an array with a key's value firste poisort the result with another key's value
KTM

1
@KTM La logica è la seguente: se entrambe le dimensioni g sono uguali, la prima parte della condizione è uguale a 0, che è considerata falsa, e la seconda parte della condizione viene eseguita.
Scalpweb

@Scalpweb Sì :) quindi funziona per ordinare un array con un numero qualsiasi di chiavi una per una, giusto ?! Bel trucco
KTM


14

Mi rendo conto che questo è stato chiesto tempo fa, ma ho pensato di aggiungere la mia soluzione.

Questa funzione genera dinamicamente metodi di ordinamento. fornire semplicemente ogni nome di proprietà figlio ordinabile, anteposto a +/- per indicare l'ordine crescente o decrescente. Super riutilizzabile e non ha bisogno di sapere nulla sulla struttura dati che hai messo insieme. Potrebbe essere una prova idiota, ma non sembra necessario.

function getSortMethod(){
    var _args = Array.prototype.slice.call(arguments);
    return function(a, b){
        for(var x in _args){
            var ax = a[_args[x].substring(1)];
            var bx = b[_args[x].substring(1)];
            var cx;

            ax = typeof ax == "string" ? ax.toLowerCase() : ax / 1;
            bx = typeof bx == "string" ? bx.toLowerCase() : bx / 1;

            if(_args[x].substring(0,1) == "-"){cx = ax; ax = bx; bx = cx;}
            if(ax != bx){return ax < bx ? -1 : 1;}
        }
    }
}

utilizzo di esempio:

items.sort (getSortMethod ('- price', '+ priority', '+ name'));

questo ordina prima dal itemspiù basso price, con i legami che vanno all'elemento con il più alto priority. ulteriori legami vengono interrotti dall'articoloname

dove items è un array come:

var items = [
    { name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
    { name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
    { name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
    { name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
    { name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 } ...
];

demo live: http://gregtaff.com/misc/multi_field_sort/

EDIT: problema risolto con Chrome.


Questo è fantastico
Azure

Risposta geniale!
Marius

per il dattiloscritto (per non ottenere un error TS2554: Expected 0 arguments, but got ..) usa la sintassi qui: stackoverflow.com/a/4116634/5287221
Chananel P

6

Mi aspetto che l' operatore ternario((aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;) ti abbia confuso. Dovresti controllare il link per capirlo meglio.

Fino ad allora, ecco il tuo codice gonfiato per intero se / altrimenti.

grouperArray.sort(function (a, b) {
    if (a.gsize < b.gsize)
    {
        return -1;
    }
    else if (a.gsize > b.gsize)
    {
        return 1;
    }
    else
    {
        if (a.glow < b.glow)
        {
            return -1;
        }
        else if (a.glow > b.glow)
        {
            return 1;
        }
        return 0;
    }
});

6

Ecco un'implementazione per coloro che potrebbero volere qualcosa di più generico che funzioni con un numero qualsiasi di campi.

Array.prototype.sortBy = function (propertyName, sortDirection) {

    var sortArguments = arguments;
    this.sort(function (objA, objB) {

        var result = 0;
        for (var argIndex = 0; argIndex < sortArguments.length && result === 0; argIndex += 2) {

            var propertyName = sortArguments[argIndex];
            result = (objA[propertyName] < objB[propertyName]) ? -1 : (objA[propertyName] > objB[propertyName]) ? 1 : 0;

            //Reverse if sort order is false (DESC)
            result *= !sortArguments[argIndex + 1] ? 1 : -1;
        }
        return result;
    });

}

Fondamentalmente, puoi specificare qualsiasi numero di nome proprietà / direzione di ordinamento:

var arr = [{
  LastName: "Doe",
  FirstName: "John",
  Age: 28
}, {
  LastName: "Doe",
  FirstName: "Jane",
  Age: 28
}, {
  LastName: "Foo",
  FirstName: "John",
  Age: 30
}];

arr.sortBy("LastName", true, "FirstName", true, "Age", false);
//Will return Jane Doe / John Doe / John Foo

arr.sortBy("Age", false, "LastName", true, "FirstName", false);
//Will return John Foo / John Doe / Jane Doe

3
grouperArray.sort(function (a, b) {
  var aSize = a.gsize;
  var bSize = b.gsize;
  var aLow = a.glow;
  var bLow = b.glow;
  console.log(aLow + " | " + bLow);      
  return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : ( (aLow < bLow ) ? -1 : (aLow > bLow ) ? 1 : 0 );
});

3
grouperArray.sort(function (a, b) {
     var aSize = a.gsize;     
     var bSize = b.gsize;     
     var aLow = a.glow;
     var bLow = b.glow;
     console.log(aLow + " | " + bLow);
     return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0); }); 

3

Ecco un'implementazione che utilizza la ricorsione per ordinare in base a un numero qualsiasi di campi di ordinamento da 1 a infinito. Gli si passa un array di risultati che è un array di oggetti risultato da ordinare e un array di ordinamento che è un array di oggetti di ordinamento che definiscono l'ordinamento. Ogni oggetto di ordinamento deve avere una chiave di "selezione" per il nome della chiave in base al quale ordina e una chiave di "ordine" che è una stringa che indica "ascendente" o "discendente".

sortMultiCompare = (a, b, sorts) => {
    let select = sorts[0].select
    let order = sorts[0].order
    if (a[select] < b[select]) {
        return order == 'ascending' ? -1 : 1
    } 
    if (a[select] > b[select]) {
        return order == 'ascending' ? 1 : -1
    }
    if(sorts.length > 1) {
        let remainingSorts = sorts.slice(1)
        return this.sortMultiCompare(a, b, remainingSorts)
    }
    return 0
}

sortResults = (results, sorts) => {
    return results.sort((a, b) => {
        return this.sortMultiCompare(a, b, sorts)
    })
}

// example inputs
const results = [
    {
        "LastName": "Doe",
        "FirstName": "John",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Doe",
        "FirstName": "Jane",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Johnson",
        "FirstName": "Kevin",
        "MiddleName": "Bill"
    }
]

const sorts = [
    {
        "select": "LastName",
        "order": "ascending"
    },
    {
        "select": "FirstName",
        "order": "ascending"
    },
    {
        "select": "MiddleName",
        "order": "ascending"
    }    
]

// call the function like this:
let sortedResults = sortResults(results, sorts)

2

Un modo dinamico per farlo con chiavi MULTIPLE:

  • filtra i valori univoci da ogni colonna / chiave di ordinamento
  • mettere in ordine o invertirlo
  • aggiungi pesi larghezza zeropad per ogni oggetto in base ai valori delle chiavi indexOf (valore)
  • ordinare utilizzando pesi caclutati

inserisci qui la descrizione dell'immagine

Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) { 
    sorts.map(sort => {            
        sort.uniques = Array.from(
            new Set(this.map(obj => obj[sort.key]))
        );

        sort.uniques = sort.uniques.sort((a, b) => {
            if (typeof a == 'string') {
                return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
            }
            else if (typeof a == 'number') {
                return sort.inverse ? (a < b) : (a > b ? 1 : 0);
            }
            else if (typeof a == 'boolean') {
                let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
                return x;
            }
            return 0;
        });
    });

    const weightOfObject = (obj) => {
        let weight = "";
        sorts.map(sort => {
            let zeropad = `${sort.uniques.length}`.length;
            weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
        });
        //obj.weight = weight; // if you need to see weights
        return weight;
    }

    this.sort((a, b) => {
        return weightOfObject(a).localeCompare( weightOfObject(b) );
    });

    return this;
}
});

Uso:

// works with string, number and boolean
let sortered = your_array.orderBy([
    {key: "type", inverse: false}, 
    {key: "title", inverse: false},
    {key: "spot", inverse: false},
    {key: "internal", inverse: true}
]);

inserisci qui la descrizione dell'immagine


1

Questo è quello che uso

function sort(a, b) {
    var _a = "".concat(a.size, a.glow);
    var _b = "".concat(b.size, b.glow);
    return _a < _b;
}

concatena i due elementi come una stringa e verranno ordinati in base a un valore stringa. Se vuoi puoi racchiudere _a e _b con parseInt per confrontarli come numeri se sai che saranno numerici.


1

Ecco la soluzione per il caso, quando si dispone di una chiave di ordinamento prioritario, che potrebbe non esistere in alcuni elementi particolari, quindi è necessario ordinare per chiavi di fallback.

Un esempio di dati di input ( id2 è la chiave di ordinamento prioritario):

const arr = [
    {id: 1},
    {id: 2, id2: 3},
    {id: 4},
    {id: 3},
    {id: 10, id2: 2},
    {id: 7},
    {id: 6, id2: 1},
    {id: 5},
    {id: 9, id2: 2},
    {id: 8},
];

E l'output dovrebbe essere:

[ { id: 6, id2: 1 },
  { id: 9, id2: 2 },
  { id: 10, id2: 2 },
  { id: 2, id2: 3 },
  { id: 1 },
  { id: 3 },
  { id: 4 },
  { id: 5 },
  { id: 7 },
  { id: 8 } ]

La funzione di confronto sarà come:

arr.sort((a,b) => {
  if(a.id2 || b.id2) {
    if(a.id2 && b.id2) {
      if(a.id2 === b.id2) {
        return a.id - b.id;
      }
      return a.id2 - b.id2;
    }
    return a.id2 ? -1 : 1;
  }
  return a.id - b.id
});

PS Nel caso in cui .id di .id2 possa essere zero, considera di usare typeof.


0
grouperArray.sort(
  function(a,b){return a.gsize == b.gsize ? a.glow - b.glow : a.gsize - b.gsize}
);

0
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    if (aSize !== aSize)
        return aSize - bSize;
    return a.glow - b.glow;
});

non testato, ma penso che dovrebbe funzionare.


0

Nel mio caso, ordino l'elenco delle notifiche per parametro "importante" e per "data"

  • passaggio 1: filtro le notifiche per "importante" e non importante

    let importantNotifications = notifications.filter(
            (notification) => notification.isImportant);
    
      let unImportantNotifications = notifications.filter(
            (notification) => !notification.isImportant);
    
  • passo 2: li ordino per data

      sortByDate = (notifications) => {
      return notifications.sort((notificationOne, notificationTwo) => {
        return notificationOne.date - notificationTwo.date;
      });
    };
    
  • passaggio 3: uniscili

    [
        ...this.sortByDate(importantNotifications),
        ...this.sortByDate(unImportantNotifications),
      ];
    
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.