Javascript: ordina l'array in base a un altro array


169

È possibile ordinare e riordinare un array che assomiglia a questo:

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

per abbinare la disposizione di questo array:

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

Sfortunatamente, non ho alcun ID su cui tenere traccia. Avrei bisogno di dare priorità all'array di articoli in modo che corrisponda il più vicino possibile al sortingArr.

Aggiornare:

Ecco l'output che sto cercando:

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

Qualche idea su come si possa fare?


Se non si vuole fare tutto manualmente, dare un'occhiata al peccato funzione di matrice PHP.js .
Adi,

Solo eseguendo il ciclo sull'ordinamento Array e riscrivendo gli elementi Array
mplungjan

6
Dove più matrici hanno lo stesso valore di ordinamento (ad es. 'B') come si decide quale elemento va posizionato nella matrice ordinata? Con 'Bob', 'Henry' e 'Thomas' che hanno tutti il ​​valore 'b' - come decidi quale va per primo, terzo e quarto?
Mitch Satchwell,

@MitchS sarebbe possibile dare la priorità alla lettura da sinistra a destra? Questo è un vero mal di testa, dal momento che non ho alcun ID da confrontare.
user1448892

Da sinistra a destra intendi l'ordine in cui compaiono negli articoli originali Array?
Mitch Satchwell,

Risposte:


74

Qualcosa di simile a:

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

Ecco un codice più breve, ma distrugge l' sortingarray:

result = items.map(function(item) {
    var n = sorting.indexOf(item[1]);
    sorting[n] = '';
    return [n, item]
}).sort().map(function(j) { return j[1] })

28
Complessità quadratica! Provalo con una grande quantità di dati ...
Julien Royer,

6
@ thg435: la complessità ha poco a che fare con l '"ottimizzazione", a meno che il volume dei dati non sia garantito (come potrebbe essere il caso qui).
Julien Royer,

2
@georg Quando si tratta della complessità degli algoritmi che agiscono su strutture di dati, l'ottimizzazione di algoritmi con complessità quadratiche (o peggio) non è mai prematura ed è sempre necessaria (a meno che non si possa garantire che le dimensioni del set di dati siano ridotte) . La differenza di prestazioni è (letteralmente) espressa in ordine di grandezza.
Abion47

238

Risposta a una riga.

itemsArray.sort(function(a, b){  
  return sortingArr.indexOf(a) - sortingArr.indexOf(b);
});

11
Questo muterà itemsArray. A seconda del requisito prestazionale, sarebbe molto più sicuro itemsArray.slice().sort(...).
Sawtaytoes,

1
il metodo di ordinamento restituisce un array. vedi developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Durgpal Singh,

8
Restituisce l'array, ma esegue anche l'ordinamento in posizione e muta l'originale.
mynameistechno,

2
questa dovrebbe essere una vera risposta
urmurmur,

6
@Morvael, ciò è dovuto a questa risposta che richiede sortingArrdi contenere tutti i valori in itemsArray. La correzione è di spingere gli elementi sul retro dell'array se non esistono 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

35

Se si utilizza la funzione di ordinamento dell'array nativo, è possibile passare un comparatore personalizzato da utilizzare durante l'ordinamento dell'array. Il comparatore dovrebbe restituire un numero negativo se il primo valore è inferiore al secondo, zero se sono uguali e un numero positivo se il primo valore è maggiore.

Quindi, se capisco l'esempio che stai dando correttamente, potresti fare qualcosa del tipo:

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

itemsArray.sort(sortFunc);

3
Ciò non funzionerà, l'ordine risultante sarebbe b, b, b, c, c, d poiché indexOfrestituisce il primo indice.
Mitch Satchwell,

Grazie, ma vorrei che l'output di itemsArray corrispondesse all'array sorting.
user1448892

6
Preferisco questa risposta se gli "ID" nel sortingArrsono unici - che, per fortuna, sono nel mio caso :)
Dillondrenzek,

3
Dovresti dichiarare l' sortingArrayesterno della funzione per poterlo ri-dichiarare ad ogni iterazione di ordinamento
aurumpotestasest

26

Caso 1: domanda originale (nessuna libreria)

Molte altre risposte che funzionano. :)

Caso 2: domanda originale (Lodash.js o Underscore.js)

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

Caso 3: ordina Array1 come se fosse Array2

Immagino che la maggior parte delle persone sia venuta qui alla ricerca di un equivalente di array_multisort di PHP (l'ho fatto), quindi ho pensato di pubblicare anche quella risposta. Ci sono un paio di opzioni:

1. Esiste un'implementazione JS esistente di array_multisort () . Grazie a @Adnan per averlo sottolineato nei commenti. È piuttosto grande, però.

2. Scrivi il tuo. ( Demo JSFiddle )

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 o Underscore.js (entrambe le librerie più piccole e popolari che si concentrano sulle prestazioni) offrono funzioni di supporto che ti consentono di fare ciò:

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

... Che (1) raggrupperà il sortArray in [index, value]coppie, (2) li ordinerà in base al valore (puoi anche fornire un callback qui), (3) sostituirà ciascuna delle coppie con l'oggetto dal itemArray nell'indice il coppia originata da.


1
Ottima soluzione, in alternativa puoi usare _.indexBy e rimuovere il turno se la tua struttura di dati è un po 'più complessa
Frozenfire

20

probabilmente è troppo tardi, ma potresti anche usare una versione modificata del codice seguente in stile ES6. Questo codice è per array come:

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

L'effettiva operazione:

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

L'operazione effettiva in ES5:

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

Dovrebbe risultare in arrayToBeSorted = [3,5]

Non distrugge l'array di riferimento.


4
Cosa succede se l'arrayToBeSorted è un array di oggetti, ovvero: {1: {…}, 2: {…}, 3: {…}, 4: {…}, 5: {…}}? ma arrayWithReferenceOrder è solo un array normale?
Crystal,

3
@sushruth come fa a ordinare l'array?
hitautodestruct,

@Cristallo, questo è un oggetto, non una matrice di oggetti. Gli elementi / elementi in un oggetto non hanno alcun ordine, ovvero il loro ordine non è impostato. Una serie di oggetti sarebbe simile [{name: "1"}, {name: "2"}, {name: "3"}, ...].
JohnK

8

Vorrei utilizzare un oggetto intermedio ( itemsMap), evitando così la complessità quadratica:

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

Vedi http://jsfiddle.net/eUskE/


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/

Ordine risultante: Bob, Jason, Henry, Thomas, Anne, Andrew


6

Perché non qualcosa del genere

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

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

La funzione mappa potrebbe non essere disponibile su tutte le versioni di Javascript


1
Questa è la soluzione più pulita e dovrebbe essere accettata la risposta. Grazie!
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)

2
Ti preghiamo di considerare di aggiungere una breve spiegazione / descrizione che spieghi perché / come questo codice risponde alla domanda.
Yannis,

3

Questo è quello che stavo cercando e ho fatto per ordinare un array di array basato su un altro array:

È attivo ^ 3 e potrebbe non essere la migliore pratica (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));
Potrebbe aiutare qualcuno


2

Ho dovuto farlo per un payload JSON che ricevo da un'API, ma non era nell'ordine desiderato.

L'array deve essere l'array di riferimento, quello per cui si desidera ordinare il secondo array in base a:

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

Ho fatto questi come oggetti perché alla fine avranno altre proprietà.

Matrice creata:

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

Usato questo con set di risultati dal database. Non so quanto sia efficiente, ma con il numero limitato di colonne che ho usato, ha funzionato bene.

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

2

Per ottenere un nuovo array ordinato, è possibile prendere un Mape raccogliere tutti gli elementi con la chiave desiderata in un array e mappare le chiavi ordinate desiderate prendendo l'elemento setacciato del gruppo desiderato.

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


HatQuesta è la mia preferita, faccio lo stesso eppure uso {}invece di 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;
})

Questa soluzione aggiungerà gli oggetti alla fine se la chiave di ordinamento non è presente nell'array di riferimento


0

questo dovrebbe funzionare:

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;

0

Potresti provare questo metodo.

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
}

0

ES6

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

Altri esempi con matrici di input diverse


0

Nel caso in cui venissi qui per fare questo con una serie di oggetti, ecco un adattamento della fantastica risposta di @Durgpal Singh:

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

-1

Utilizzare il metodo $ .inArray () da jQuery. Quindi potresti fare qualcosa del genere

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

-1

Utilizzare l'intersezione di due matrici.

Ex:

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

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

_.intersection(sortArray, arrayToBeSort) 

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

se 'z e' s 'sono al di fuori dell'intervallo del primo array, aggiungerlo alla fine del risultato


-4

Puoi fare qualcosa del genere:

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

Puoi provarlo qui .

Nota: questo presuppone che le matrici in cui passi siano di dimensioni equivalenti, in caso contrario potrebbe essere necessario aggiungere alcuni controlli aggiuntivi.

fare riferimento al collegamento

fare riferimento


Questo ha bisogno di essere modificato. non solo il jfiddle restituisce il risultato sbagliato, i nomi degli argomenti della funzione non corrispondono al contenuto interno?
twobob,
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.