2015-04-17 13 views
9

Sagen, ich habe eine Reihe von Person-Objekte:Sortierung ein Array durch den Inhalt eines anderen in JavaScript

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

und ich habe eine Funktion, die unsensibel ein Array von Strings Fall sortieren:

function caseInsensitiveSort(arr) { ... } 

Gibt es eine einfache Möglichkeit, meine vorhandene Sortierfunktion mit Array.prototype.map zu kombinieren, um das Array people mit dem Schlüssel name zu sortieren?

I.e. es würde produzieren

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

es von Hand tun nicht schwer, in diesem besonderen Fall ist,

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

aber ich kann nicht denken Sie an einen Weg, es zu tun, die mir die prä- zu verwenden erlauben würde vorhandene Sortierfunktion. In einem Fall, in dem die Sortierfunktion mehr angepasst ist, wäre dies nützlich.

Edit: Ich glaube, dass das Kernproblem hier ist, dass, um dies gut zu machen, Sie herausfinden müssen, was die ursprünglichen Indizes zugeordnet wurden, wenn Sie das Proxy-Array sortieren. Diese neuen Indizes mit der systemeigenen sort-Funktion zu erhalten, scheint im allgemeinen Fall nicht möglich zu sein. Aber ich würde mich freuen, falsch bewiesen zu werden.

Edit: Die Art, wie ich das versuchte, ist zu ineffizient, um nützlich zu sein. Siehe die Antwort unten für die Lösung, die stattdessen eine Vergleichsfunktion verwendet.

+0

Wenn "caseInsensitiveSort" ein Array akzeptiert, müssten Sie ein Array der Namen an diese Funktion übergeben, die Namen sortieren und dann das Array mit den Objekten basierend auf dem Array mit den Namen sortieren. Klingt nach einer wirklich komplizierten Art, etwas Einfaches zu tun. – adeneo

+0

Wird es mit wenigen Elementen umgehen? Da die Methode 'Array.prototype.map' ein neues Array erstellt, ist es bei Millionen von Datensätzen am besten, sie direkt zu sortieren. –

+0

@adeneo @Jordan Sie sind beide richtig. Wie xdazz unten ausgeführt hat, besteht die richtige Methode, dies effizient zu tun, darin, die Logik meines Vergleichs in eine separate Funktion zu abstrahieren und diese an "Array.prototype.sort" zu liefern, anstatt zu versuchen, meine Sortierfunktion einzukeilen. –

Antwort

4

Sie könnten Ihre vorhandene Funktion verwenden, um ein Array sortierter Namen zu erhalten, und dann das Array people sortieren, indem Sie den Index im Array sortierte Namen vergleichen.

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

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

Aber das ist nicht effizient ist, sollten Sie zu abstrahieren ausprobieren die Logik vergleichen von der caseInsensitiveSort Funktion in eine caseInsensitiveCompare Funktion.

Dann würde Ihr Beispiel werden:

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

Die Verwendung von 'indexOf' zweimal pro Sortier-Iteration würde dies für ein großes Array ziemlich langsam machen, aber es ist wahrscheinlich die einzige Möglichkeit, dies allgemein zu tun. +1. –

+1

@ChrisMiddleton Können Sie die Vergleichslogik nicht von der Funktion 'caseInsensitiveSort 'abstrahieren? – xdazz

+0

Ja, du hast Recht. Das ist die richtige Antwort, denke ich. Um dies auf allgemeine und effiziente Weise zu tun, müssen Sie auf der Ebene der Vergleichsfunktion und nicht der Sortierfunktion arbeiten. Vielen Dank. –

1

Je nachdem, wie Ihre caseInsensitiveSort() Funktion arbeitet, Sie verwenden ein .toString() Verfahren machen könnte, dies zu tun:

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

caseInsensitiveSort(toSort); 

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

Wenn das keine Option ist ein unordentlicher, aber immer noch effizienter Ansatz, die Namen zu sortieren, sie ihren Indizes zuzuordnen und dann danach zu sortieren:

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

Vielen Dank für das Hinzufügen dieser - Ihre zweite Lösung ist ähnlich zu dem, was ich dachte, als ich fragte. Es funktioniert natürlich nur, wenn die Werte, die Sie sortieren, keyable Werte sind, aber dies ist wahrscheinlich eine unvermeidliche Einschränkung, es sei denn, Sie verwendeten Hash-Werte für nicht-keyable Werte. –

+0

@ChrisMiddleton Ja, das ist sicherlich wahr. Es funktioniert für Namen, würde aber nicht für alle Arten von Werten funktionieren. – JLRishe

Verwandte Themen