Javascript - Array nach einem anderen Array sortieren

166

Ist es möglich, ein Array zu sortieren und neu anzuordnen, das so aussieht:

itemsArray = [ 
    ['Anne', 'a'],
    ['Bob', 'b'],
    ['Henry', 'b'],
    ['Andrew', 'd'],
    ['Jason', 'c'],
    ['Thomas', 'b']
]

passend zur Anordnung dieses Arrays:

sortingArr = [ 'b', 'c', 'b', 'b', 'a', 'd' ]

Leider habe ich keine IDs, die ich nachverfolgen könnte. Ich müsste das Items-Array priorisieren, um dem sortingArr so nahe wie möglich zu kommen.

Aktualisieren:

Hier ist die Ausgabe, die ich suche:

itemsArray = [    
    ['Bob', 'b'],
    ['Jason', 'c'],
    ['Henry', 'b'],
    ['Thomas', 'b']
    ['Anne', 'a'],
    ['Andrew', 'd'],
]

Irgendeine Idee, wie das gemacht werden kann?

user1448892
quelle
Wenn Sie nicht alles manuell ausführen möchten, sehen Sie sich die Array-Funktion sin PHP.js an .
Adi
Nur durch Schleifen über das sortingArray und das Umschreiben der itemsArray
mplungjan
6
Wenn mehrere Arrays den gleichen Sortierwert haben (dh 'b'), wie entscheiden Sie, welches Element im sortierten Array wohin geht? Mit 'Bob', 'Henry' und 'Thomas', die alle den Wert 'b' haben - wie entscheiden Sie, was zuerst, drittens und viertens geht?
Mitch Satchwell
@MitchS wäre es möglich, das Lesen von links nach rechts zu priorisieren? Dies ist ein echtes Problem, da ich keine zu vergleichenden IDs habe.
user1448892
Mit von links nach rechts meinen Sie die Reihenfolge, in der sie im Original angezeigt werden.
Mitch Satchwell

Antworten:

74

Etwas wie:

items = [ 
    ['Anne', 'a'],
    ['Bob', 'b'],
    ['Henry', 'b'],
    ['Andrew', 'd'],
    ['Jason', 'c'],
    ['Thomas', 'b']
]

sorting = [ 'b', 'c', 'b', 'b', 'c', 'd' ];
result = []

sorting.forEach(function(key) {
    var found = false;
    items = items.filter(function(item) {
        if(!found && item[1] == key) {
            result.push(item);
            found = true;
            return false;
        } else 
            return true;
    })
})

result.forEach(function(item) {
    document.writeln(item[0]) /// Bob Jason Henry Thomas Andrew
})

Hier ist ein kürzerer Code, der jedoch das sortingArray zerstört :

result = items.map(function(item) {
    var n = sorting.indexOf(item[1]);
    sorting[n] = '';
    return [n, item]
}).sort().map(function(j) { return j[1] })
georg
quelle
27
Quadratische Komplexität! Versuchen Sie es mit einer großen Datenmenge…
Julien Royer
6
@ thg435: Komplexität hat wenig mit "Optimierung" zu tun, es sei denn, das Datenvolumen ist garantiert gering (was hier der Fall sein kann).
Julien Royer
2
@georg Wenn es um die Komplexität von Algorithmen geht, die auf Datenstrukturen einwirken, ist die Optimierung von Algorithmen mit quadratischer (oder schlechterer) Komplexität niemals verfrüht und immer notwendig (es sei denn, Sie können garantieren, dass die Größe des Datensatzes klein sein wird). . Der Leistungsunterschied wird (im wahrsten Sinne des Wortes) in Größenordnungen ausgedrückt.
Abion47
235

Einzeilige Antwort.

itemsArray.sort(function(a, b){  
  return sortingArr.indexOf(a) - sortingArr.indexOf(b);
});
Durgpal Singh
quelle
10
Dies wird mutieren itemsArray. Abhängig von den Leistungsanforderungen wäre dies viel sicherer itemsArray.slice().sort(...).
Sawtaytoes
1
Sortiermethode gibt ein Array zurück. siehe developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Durgpal Singh
8
Es gibt zwar das Array zurück, sortiert aber auch an Ort und Stelle und mutiert das Original.
mynameistechno
2
Dies sollte eine echte Antwort sein
Urmurmur
6
@Morvael, dies liegt an dieser Antwort, sortingArrbei der alle Werte in enthalten sein müssen itemsArray. Die Lösung besteht darin, Elemente an die Rückseite des Arrays zu verschieben, wenn sie nicht vorhanden sind in sortingArr:allProducts.sort((product1, product2) => { const index1 = manualSort.indexOf(product1.id); const index2 = manualSort.indexOf(product2.id); return ( (index1 > -1 ? index1 : Infinity) - (index2 > -1 ? index2 : Infinity) ); });
Freshollie
34

Wenn Sie die native Array-Sortierfunktion verwenden, können Sie einen benutzerdefinierten Komparator übergeben, der beim Sortieren des Arrays verwendet wird. Der Komparator sollte eine negative Zahl zurückgeben, wenn der erste Wert kleiner als der zweite ist, Null, wenn sie gleich sind, und eine positive Zahl, wenn der erste Wert größer ist.

Wenn ich das Beispiel, das Sie geben, richtig verstehe, können Sie Folgendes tun:

function sortFunc(a, b) {
  var sortingArr = [ 'b', 'c', 'b', 'b', 'c', 'd' ];
  return sortingArr.indexOf(a[1]) - sortingArr.indexOf(b[1]);
}

itemsArray.sort(sortFunc);
David Lewis
quelle
3
Das wird nicht funktionieren, die resultierende Reihenfolge wäre b, b, b, c, c, d, da indexOfder erste Index zurückgegeben wird.
Mitch Satchwell
Vielen Dank, aber ich möchte, dass die Ausgabe von itemsArray mit der sortingArray übereinstimmt.
user1448892
6
Ich bevorzuge diese Antwort, wenn die "IDs" in der sortingArreindeutig sind - was sie zum Glück in meinem Fall sind :)
dillondrenzek
3
Sie sollten die sortingArrayAußenseite der Funktion deklarieren, um sie bei jeder
Sortieriteration
26

Fall 1: Originalfrage (keine Bibliotheken)

Viele andere Antworten, die funktionieren. :) :)

Fall 2: Ursprüngliche Frage (Lodash.js oder Underscore.js)

var groups = _.groupBy(itemArray, 1);
var result = _.map(sortArray, function (i) { return groups[i].shift(); });

Fall 3: Sortieren Sie Array1 so, als wäre es Array2

Ich vermute, dass die meisten Leute hierher gekommen sind, um nach einem Äquivalent zu array_multisort von PHP zu suchen (ich habe es getan), also dachte ich, ich würde diese Antwort auch posten. Es gibt einige Möglichkeiten:

1. Es gibt eine JS-Implementierung von array_multisort () . Vielen Dank an @Adnan für den Hinweis in den Kommentaren. Es ist jedoch ziemlich groß.

2. Schreiben Sie Ihre eigenen. ( JSFiddle-Demo )

function refSort (targetData, refData) {
  // Create an array of indices [0, 1, 2, ...N].
  var indices = Object.keys(refData);

  // Sort array of indices according to the reference data.
  indices.sort(function(indexA, indexB) {
    if (refData[indexA] < refData[indexB]) {
      return -1;
    } else if (refData[indexA] > refData[indexB]) {
      return 1;
    }
    return 0;
  });

  // Map array of indices to corresponding values of the target array.
  return indices.map(function(index) {
    return targetData[index];
  });
}

3. Lodash.js oder Underscore.js (beide beliebte, kleinere Bibliotheken, die sich auf die Leistung konzentrieren) bieten tun können:

    var result = _.chain(sortArray)
      .pairs()
      .sortBy(1)
      .map(function (i) { return itemArray[i[0]]; })
      .value();

... die (1) das sortArray in [index, value]Paare gruppiert , (2) sie nach dem Wert sortiert (Sie können hier auch einen Rückruf bereitstellen), (3) jedes der Paare durch das Element aus dem itemArray am Index der ersetzt Paar entstand aus.

Don McCurdy
quelle
1
Ausgezeichnete Lösung, alternativ können Sie _.indexBy verwenden und die Verschiebung entfernen, wenn Ihre Datenstruktur etwas komplexer ist
Frozenfire
20

Dies ist wahrscheinlich zu spät, aber Sie können auch eine modifizierte Version des folgenden Codes im ES6-Stil verwenden. Dieser Code ist für Arrays wie:

var arrayToBeSorted = [1,2,3,4,5];
var arrayWithReferenceOrder = [3,5,8,9];

Die eigentliche Operation:

arrayToBeSorted = arrayWithReferenceOrder.filter(v => arrayToBeSorted.includes(v));

Der tatsächliche Betrieb in ES5:

arrayToBeSorted = arrayWithReferenceOrder.filter(function(v) {
    return arrayToBeSorted.includes(v);
});

Sollte dazu führen arrayToBeSorted = [3,5]

Zerstört das Referenzarray nicht.

Sushruth
quelle
4
Was ist, wenn das ArrayToBeSorted ein Array von Objekten ist, dh: {1: {…}, 2: {…}, 3: {…}, 4: {…}, 5: {…}}? aber das arrayWithReferenceOrder ist nur ein normales Array?
Crystal
3
@sushruth Wie sortiert das Array?
Hitautodestruct
@Crystal, das ist ein Objekt, kein Array von Objekten. Die Elemente / Elemente in einem Objekt haben keine Reihenfolge, dh ihre Reihenfolge ist nicht festgelegt. Eine Reihe von Objekten würde ungefähr so ​​aussehen [{name: "1"}, {name: "2"}, {name: "3"}, ...].
JohnK
8

Ich würde ein Zwischenobjekt ( itemsMap) verwenden, um quadratische Komplexität zu vermeiden:

function createItemsMap(itemsArray) { // {"a": ["Anne"], "b": ["Bob", "Henry"], …}
  var itemsMap = {};
  for (var i = 0, item; (item = itemsArray[i]); ++i) {
    (itemsMap[item[1]] || (itemsMap[item[1]] = [])).push(item[0]);
  }
  return itemsMap;
}

function sortByKeys(itemsArray, sortingArr) {
  var itemsMap = createItemsMap(itemsArray), result = [];
  for (var i = 0; i < sortingArr.length; ++i) {
    var key = sortingArr[i];
    result.push([itemsMap[key].shift(), key]);
  }
  return result;
}

Siehe http://jsfiddle.net/eUskE/

Julien Royer
quelle
6
var sortedArray = [];
for(var i=0; i < sortingArr.length; i++) {
    var found = false;
    for(var j=0; j < itemsArray.length && !found; j++) {
        if(itemsArray[j][1] == sortingArr[i]) {
            sortedArray.push(itemsArray[j]);
            itemsArray.splice(j,1);
            found = true;
        }
    }
}

http://jsfiddle.net/s7b2P/

Resultierende Reihenfolge: Bob, Jason, Henry, Thomas, Anne, Andrew

Mitch Satchwell
quelle
4

Warum nicht so etwas

//array1: array of elements to be sorted
//array2: array with the indexes

array1 = array2.map((object, i) => array1[object]);

Die Kartenfunktion ist möglicherweise nicht in allen Versionen von verfügbar Javascript

Luca Di Liello
quelle
Dies ist die sauberste Lösung und sollte als Antwort akzeptiert werden. Vielen Dank!
Newman
3
let a = ['A', 'B', 'C' ]

let b = [3, 2, 1]

let c = [1.0, 5.0, 2.0]

// these array can be sorted by sorting order of b

const zip = rows => rows[0].map((_, c) => rows.map(row => row[c]))

const sortBy = (a, b, c) => {
  const zippedArray = zip([a, b, c])
  const sortedZipped = zippedArray.sort((x, y) => x[1] - y[1])

  return zip(sortedZipped)
}

sortBy(a, b, c)
Harshal Patil
quelle
2
Bitte fügen Sie eine kurze Erklärung / Beschreibung hinzu, in der erläutert wird, warum / wie dieser Code die Frage beantwortet.
Yannis
3

Dies ist, wonach ich gesucht habe und was ich getan habe, um ein Array von Arrays basierend auf einem anderen Array zu sortieren:

Es ist auf ^ 3 und möglicherweise nicht die beste Vorgehensweise (ES6)

function sortArray(arr, arr1){
      return arr.map(item => {
        let a = [];
        for(let i=0; i< arr1.length; i++){
          for (const el of item) {
            if(el == arr1[i]){
              a.push(el);
            }   
            }
          }
          return a;
      });
    }
    
    const arr1 = ['fname', 'city', 'name'];
  const arr = [['fname', 'city', 'name'],
  ['fname', 'city', 'name', 'name', 'city','fname']];
  console.log(sortArray(arr,arr1));
Es könnte jemandem helfen

El.
quelle
2

Ich musste dies für eine JSON-Nutzlast tun, die ich von einer API erhalte, aber es war nicht in der Reihenfolge, in der ich es wollte.

Array ist das Referenzarray, das zweite Array, sortiert nach:

var columns = [
    {last_name: "last_name"},
    {first_name: "first_name"},
    {book_description: "book_description"},
    {book_id: "book_id"},
    {book_number: "book_number"},
    {due_date: "due_date"},
    {loaned_out: "loaned_out"}
];

Ich habe diese als Objekte gemacht, weil diese irgendwann andere Eigenschaften haben werden.

Erstelltes Array:

 var referenceArray= [];
 for (var key in columns) {
     for (var j in columns[key]){
         referenceArray.push(j);
     }
  }

Wird mit der Ergebnismenge aus der Datenbank verwendet. Ich weiß nicht, wie effizient es ist, aber mit den wenigen Spalten, die ich verwendet habe, hat es gut funktioniert.

result.forEach((element, index, array) => {                            
    var tr = document.createElement('tr');
    for (var i = 0; i < referenceArray.length - 1; i++) {
        var td = document.createElement('td');
        td.innerHTML = element[referenceArray[i]];
        tr.appendChild(td);

    }
    tableBody.appendChild(tr);
}); 
Johnny
quelle
2

Um ein neues geordnetes Array zu erhalten, können Sie ein MapElement nehmen und alle Elemente mit dem gewünschten Schlüssel in einem Array sammeln und die gewünschten geordneten Schlüssel zuordnen, indem Sie das gesiebte Element der gewünschten Gruppe verwenden.

var itemsArray = [['Anne', 'a'], ['Bob', 'b'], ['Henry', 'b'], ['Andrew', 'd'], ['Jason', 'c'], ['Thomas', 'b']],
    sortingArr = [ 'b', 'c', 'b', 'b', 'a', 'd' ],
    map = itemsArray.reduce((m, a) => m.set(a[1], (m.get(a[1]) || []).concat([a])), new Map),
    result = sortingArr.map(k => (map.get(k) || []).shift());

console.log(result);

Nina Scholz
quelle
HatDas ist mein Favorit, ich mache das gleiche, aber benutze es {}anstelle von Map🤷‍♂️
Can Rau
2
let sortedOrder = [ 'b', 'c', 'b', 'b' ]
let itemsArray = [ 
    ['Anne', 'a'],
    ['Bob', 'b'],
    ['Henry', 'b'],
    ['Andrew', 'd'],
    ['Jason', 'c'],
    ['Thomas', 'b']
]
a.itemsArray(function (a, b) {
    let A = a[1]
    let B = b[1]

    if(A != undefined)
        A = A.toLowerCase()

    if(B != undefined)
        B = B.toLowerCase()

    let indA = sortedOrder.indexOf(A)
    let indB = sortedOrder.indexOf(B)

    if (indA == -1 )
        indA = sortedOrder.length-1
    if( indB == -1)
        indB = sortedOrder.length-1

    if (indA < indB ) {
        return -1;
    } else if (indA > indB) {
        return 1;
    }
    return 0;
})

Diese Lösung hängt die Objekte am Ende an, wenn der Sortierschlüssel nicht im Referenzarray vorhanden ist

JD-V
quelle
0

das sollte funktionieren:

var i,search, itemsArraySorted = [];
while(sortingArr.length) {
    search = sortingArr.shift();
    for(i = 0; i<itemsArray.length; i++) {
        if(itemsArray[i][1] == search) {
            itemsArraySorted.push(itemsArray[i]);
            break;
        }
    } 
}

itemsArray = itemsArraySorted;
Luca Rainone
quelle
0

Sie könnten diese Methode ausprobieren.

const sortListByRanking = (rankingList, listToSort) => {
  let result = []

  for (let id of rankingList) {
    for (let item of listToSort) {
      if (item && item[1] === id) {
        result.push(item)
      }
    }
  }

  return result
}
Holger Tidemand
quelle
0

ES6

const arrayMap = itemsArray.reduce(
  (accumulator, currentValue) => ({
    ...accumulator,
    [currentValue[1]]: currentValue,
  }),
  {}
);
const result = sortingArr.map(key => arrayMap[key]);

Weitere Beispiele mit verschiedenen Eingabearrays

Kann Rau
quelle
0

Für den Fall, dass Sie hierher kommen und dies mit einer Reihe von Objekten tun müssen, finden Sie hier eine Adaption von @Durgpal Singhs großartiger Antwort:

const itemsArray = [
  { name: 'Anne', id: 'a' },
  { name: 'Bob', id: 'b' },
  { name: 'Henry', id: 'b' },
  { name: 'Andrew', id: 'd' },
  { name: 'Jason', id: 'c' },
  { name: 'Thomas', id: 'b' }
]

const sortingArr = [ 'b', 'c', 'b', 'b', 'a', 'd' ]

Object.keys(itemsArray).sort((a, b) => {
  return sortingArr.indexOf(itemsArray[a].id) - sortingArr.indexOf(itemsArray[b].id);
})
user2521295
quelle
-1

Verwenden Sie die $ .inArray () -Methode von jQuery. Sie könnten dann so etwas tun

var sortingArr = [ 'b', 'c', 'b', 'b', 'c', 'd' ];
var newSortedArray = new Array();

for(var i=sortingArr.length; i--;) {
 var foundIn = $.inArray(sortingArr[i], itemsArray);
 newSortedArray.push(itemsArray[foundIn]);
}
toxicate20
quelle
-1

Verwenden Sie den Schnittpunkt zweier Arrays.

Ex:

var sortArray = ['a', 'b', 'c',  'd', 'e'];

var arrayToBeSort = ['z', 's', 'b',  'e', 'a'];

_.intersection(sortArray, arrayToBeSort) 

=> ['a', 'b', 'e']

Wenn 'z' und 's' außerhalb des Bereichs des ersten Arrays liegen, hängen Sie es am Ende des Ergebnisses an

Joe.CK
quelle
-4

Sie können so etwas tun:

function getSorted(itemsArray , sortingArr ) {
  var result = [];
  for(var i=0; i<arr.length; i++) {
    result[i] = arr[sortArr[i]];
  }
  return result;
}

Sie können es hier testen .

Hinweis: Dies setzt voraus, dass die von Ihnen übergebenen Arrays gleich groß sind. Wenn dies nicht der Fall ist, müssen Sie einige zusätzliche Überprüfungen hinzufügen.

siehe Link

verweisen

Gadde
quelle
Dies muss dringend bearbeitet werden. Gibt die jfiddle nicht nur das falsche Ergebnis zurück, die Namen der Funktionsargumente stimmen nicht mit dem inneren Inhalt überein?
Twobob