2015-04-17 11 views
9

dire che ho un array di oggetti persona:Ordina un array il contenuto di un'altra in JavaScript

var people = [{name: "Joe Schmo", age: 36}, {name: "JANE DOE", age: 40}]; 

e ho una funzione che può ordinare un array di caso stringhe insensitively:

function caseInsensitiveSort(arr) { ... } 

Esiste un modo semplice per combinare la mia funzione di ordinamento esistente con Array.prototype.map per ordinare l'array people usando solo la chiave name?

I.e. produrrebbe

var people = [{name: "JANE DOE", age: 40}, {name: "Joe Schmo", age: 36}]; 

farlo a mano non è difficile in questo caso particolare,

people.sort(function (a, b) { 
    return a.name.localeCompare(b.name); 
}); 

ma non riesco a pensare ad un modo di farlo che mi avrebbe permesso di usare il pre- funzione di ordinamento esistente. In un caso in cui la funzione di ordinamento è più personalizzata, sarebbe utile.

Edit: Credo che il problema principale qui è che per fare questo bene, è necessario essere in grado di capire a cosa sono stati mappati gli indici originali quando si ordina l'array proxy. Ottenere quei nuovi indici usando la funzione nativa di JS sort non sembra possibile nel caso generale. Ma sarei felice di essere smentito.

Modifica: Il modo in cui stavo cercando di farlo è troppo inefficiente per essere utile. Vedere la risposta sotto per la soluzione usando invece una funzione di confronto.

+0

Se 'caseInsensitiveSort' accetta un array, è necessario passare una matrice dei nomi a tale funzione, ordinare i nomi e quindi ordinare l'array con gli oggetti in base all'array con i nomi. Sembra un modo davvero complicato per fare qualcosa di semplice. – adeneo

+0

Gestirà pochi elementi? Perché il metodo 'Array.prototype.map' crea un nuovo array, quindi con milioni di record l'opzione migliore è ordinarla sul posto. –

+0

@adeneo @Jordan Siete entrambi corretti. Come xdazz ha sottolineato di seguito, il modo corretto per farlo in modo efficiente è di astrarre la logica del mio confronto in una funzione separata, e fornire quella a 'Array.prototype.sort', piuttosto che cercare di incuneare la mia funzione di ordinamento in. –

risposta

4

È possibile utilizzare la funzione esistente per ottenere una matrice di nomi ordinati, quindi ordinare l'array people confrontando l'indice nella matrice dei nomi ordinati.

var names = caseInsensitiveSort(people.map(function(person) { 
    return person.name; 
})); 

people.sort(function (a, b) { 
    return names.indexOf(a.name) - names.indexOf(b.name); 
}); 

ma questo non è efficiente, si dovrebbe cercare di astrarre la logica confrontare dalla funzione caseInsensitiveSort in una funzione caseInsensitiveCompare.

Allora il tuo esempio potrebbe diventare:

people.sort(function (a, b) { 
    return caseInsensitiveCompare(a.name, b.name); 
}); 
+0

L'uso di 'indexOf' due volte per l'iterazione di ordinamento renderebbe questo piuttosto lento per un grande array, ma probabilmente è l'unico modo possibile di farlo in un modo generale. +1. –

+1

@ChrisMiddleton Non puoi astrarre la logica di confronto dalla funzione 'caseInsensitiveSort'? – xdazz

+0

Sì, hai ragione. Questa è la vera risposta che penso. Per fare ciò in modo generale ed efficiente, è necessario lavorare a livello della funzione di confronto, non della funzione di ordinamento. Grazie. –

1

A seconda di come funziona la funzione caseInsensitiveSort(), si potrebbe fare uso di un metodo .toString() per fare questo:

var toSort = people.map(function (person) { 
    return { 
     person: person, 
     toString: function() { 
      return person.name; 
     } 
    }; 
}); 

caseInsensitiveSort(toSort); 

people = toSort.map(function (item) { return item.person; }); 

Se questo non è un'opzione , un approccio più confuso ma ancora efficiente è quello di ordinare i nomi, mapparli ai loro indici e quindi ordinare in base a quello:

var names = people.map(function (person) { return person.name; }); 

caseInsensitiveSort(names); 

var nameMap = {}; 
names.forEach(function (name, i) { 
    nameMap[name] = i; 
}); 

people.sort(function (a, b) { 
    return nameMap[a.name] - nameMap[b.name]; 
}); 
+0

Grazie per aver aggiunto questo - la tua seconda soluzione è simile a quello che stavo pensando quando ho chiesto. Naturalmente funziona solo se i valori che si stanno ordinando sono valori chiari, ma probabilmente si tratta di una limitazione inevitabile, a meno che non si siano utilizzati valori hash per valori non modificabili. –

+0

@ChrisMiddleton Sì, è certamente vero. Funziona per i nomi, ma non funzionerebbe per tutti i tipi di valori. – JLRishe

Problemi correlati