Javascript - mengurutkan array berdasarkan array lain

167

Apakah mungkin untuk mengurutkan dan mengatur ulang array yang terlihat seperti ini:

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

untuk mencocokkan pengaturan array ini:

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

Sayangnya, saya tidak memiliki ID untuk melacak. Saya perlu memprioritaskan item-array agar sesuai dengan sortingArr sedekat mungkin.

Memperbarui:

Ini adalah output yang saya cari:

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

Adakah yang tahu bagaimana ini bisa dilakukan?

pengguna1448892
sumber
Jika Anda tidak ingin melakukan semuanya secara manual, lihatlah dosa fungsi array yang PHP.js .
Adi
Hanya dengan mengulangi sortingArray dan menulis ulang itemsArray
mplungjan
6
Di mana banyak array memiliki nilai pengurutan yang sama (yaitu 'b') bagaimana Anda memutuskan item mana yang digunakan di dalam array yang diurutkan? Dengan 'Bob', 'Henry' dan 'Thomas' yang semuanya memiliki nilai 'b' - bagaimana Anda memutuskan mana yang lebih dulu, ketiga dan keempat?
Mitch Satchwell
@Itch apakah mungkin memprioritaskan membaca dari kiri ke kanan? Ini benar-benar sakit kepala, karena saya tidak punya ID untuk membandingkan.
user1448892
Dari kiri ke kanan maksud Anda urutannya muncul di itemsArray asli?
Mitch Satchwell

Jawaban:

74

Sesuatu seperti:

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

Berikut kode yang lebih pendek, tetapi merusak sortingarray:

result = items.map(function(item) {
    var n = sorting.indexOf(item[1]);
    sorting[n] = '';
    return [n, item]
}).sort().map(function(j) { return j[1] })
georg
sumber
27
Kompleksitas kuadratik! Cobalah dengan sejumlah besar data ...
Julien Royer
6
@ thg435: kompleksitas tidak ada hubungannya dengan "optimisasi", kecuali volume data dijamin kecil (yang mungkin terjadi di sini).
Julien Royer
2
@georg Ketika datang ke kompleksitas algoritma yang bekerja pada struktur data, optimalisasi algoritma dengan kompleksitas kuadrat (atau lebih buruk) tidak pernah prematur dan selalu diperlukan (kecuali Anda dapat menjamin ukuran set data akan menjadi kecil) . Perbedaan kinerja (secara harfiah) dinyatakan dalam urutan besarnya.
Abion47
236

Satu baris jawaban.

itemsArray.sort(function(a, b){  
  return sortingArr.indexOf(a) - sortingArr.indexOf(b);
});
Durgpal Singh
sumber
10
Ini akan bermutasi itemsArray. Tergantung pada persyaratan kinerja, itu akan jauh lebih aman untuk dilakukan itemsArray.slice().sort(...).
Sawtaytoes
1
metode sort mengembalikan array. lihat developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Durgpal Singh
8
Itu mengembalikan array, tetapi juga melakukan pengurutan di tempat dan bermutasi yang asli.
mynameistechno
2
ini harus menjadi jawaban nyata
urmurmur
6
@Morvael, ini karena jawaban ini harus sortingArrberisi semua nilai di itemsArray. Cara mengatasinya adalah mendorong item ke bagian belakang array jika tidak ada di 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

Jika Anda menggunakan fungsi pengurutan array asli, Anda bisa meneruskan pembanding khusus untuk digunakan saat mengurutkan array. Komparator harus mengembalikan angka negatif jika nilai pertama kurang dari yang kedua, nol jika mereka sama, dan angka positif jika nilai pertama lebih besar.

Jadi jika saya mengerti contoh yang Anda berikan dengan benar, Anda dapat melakukan sesuatu seperti:

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
sumber
3
Itu tidak akan berhasil, urutan yang dihasilkan akan b, b, b, c, c, d sebagai indexOfmengembalikan indeks pertama.
Mitch Satchwell
Terima kasih, tapi saya ingin output dari itemsArray agar sesuai dengan sortingArray.
user1448892
6
Saya lebih suka jawaban ini jika "id" dalam sortingArrunik - yang, untungnya, mereka dalam kasus saya :)
dillondrenzek
3
Anda harus mendeklarasikan bagian sortingArrayluar fungsi untuk avoir mendeklarasikan ulang pada setiap iterasi pengurutan
aurumpotestasest
26

Kasus 1: Pertanyaan Asli (Tidak Ada Perpustakaan)

Banyak jawaban lain yang berfungsi. :)

Kasus 2: Pertanyaan Asli (Lodash.js atau Underscore.js)

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

Kasus 3: Urutkan Array1 seolah-olah Array2

Saya menduga bahwa kebanyakan orang datang ke sini mencari yang setara dengan PHP's array_multisort (saya lakukan), jadi saya pikir saya akan memposting jawaban itu juga. Ada beberapa opsi:

1. Ada implementasi JS yang ada dari array_multisort () . Terima kasih kepada @Adnan karena menunjukkannya di komentar. Tapi ini cukup besar.

2. Tulis milik Anda sendiri. ( 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 atau Underscore.js (keduanya populer, perpustakaan kecil yang berfokus pada kinerja) menawarkan fungsi pembantu yang memungkinkan Anda untuk melakukan ini:

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

... Yang akan (1) mengelompokkan sortArray menjadi [index, value]pasangan, (2) mengurutkannya berdasarkan nilai (Anda juga dapat memberikan panggilan balik di sini), (3) mengganti setiap pasangan dengan item dari itemArray pada indeks yang pasangan berasal dari.

Don McCurdy
sumber
1
Solusi luar biasa, atau Anda dapat menggunakan _.indexBy dan menghapus shift jika struktur data Anda sedikit lebih kompleks
Frozenfire
20

ini mungkin terlambat tetapi, Anda juga dapat menggunakan beberapa versi kode yang dimodifikasi di bawah ini dalam gaya ES6. Kode ini untuk array seperti:

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

Operasi yang sebenarnya:

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

Operasi aktual di ES5:

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

Harus menghasilkan arrayToBeSorted = [3,5]

Tidak menghancurkan array referensi.

Sushruth
sumber
4
Bagaimana jika saya arrayToBeSorted adalah Array Objek yaitu: {1: {...}, 2: {...}, 3: {...}, 4: {...}, 5: {...}}? tetapi arrayWithReferenceOrder hanyalah array normal?
Crystal
3
@sushruth bagaimana ini mengurutkan array?
hitautodestruct
@Crystal, itu objek, bukan array objek. Elemen / item dalam suatu objek tidak memiliki urutan, yaitu, urutannya tidak ditetapkan. Array objek akan terlihat seperti [{name: "1"}, {name: "2"}, {name: "3"}, ...].
JohnK
8

Saya akan menggunakan objek perantara ( itemsMap), sehingga menghindari kompleksitas kuadratik:

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

Lihat http://jsfiddle.net/eUskE/

Julien Royer
sumber
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/

Urutan yang dihasilkan: Bob, Jason, Henry, Thomas, Anne, Andrew

Mitch Satchwell
sumber
6

Kenapa tidak seperti itu

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

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

Fungsi peta mungkin tidak tersedia di semua versi Javascript

Luca Di Liello
sumber
1
Ini adalah solusi terbersih, dan harus diterima jawabannya. Terima kasih!
pemain baru
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
sumber
2
Silakan pertimbangkan untuk menambahkan penjelasan / deskripsi singkat yang menjelaskan mengapa / bagaimana kode ini menjawab pertanyaan.
Yannis
3

Inilah yang saya cari dan saya lakukan untuk mengurutkan Array of Array berdasarkan Array lain:

Ini Aktif ^ 3 dan mungkin bukan praktik terbaik (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));
Mungkin membantu seseorang

El.
sumber
2

Saya harus melakukan ini untuk muatan JSON yang saya terima dari API, tetapi itu tidak sesuai urutan yang saya inginkan.

Array menjadi array referensi, yang Anda inginkan array kedua diurutkan berdasarkan:

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

Saya melakukan ini sebagai objek karena ini akan memiliki properti lain pada akhirnya.

Array yang dibuat:

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

Digunakan ini dengan set hasil dari database. Saya tidak tahu seberapa efisien itu, tetapi dengan beberapa kolom yang saya gunakan, itu berfungsi dengan baik.

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
sumber
2

Untuk mendapatkan array yang dipesan baru, Anda bisa mengambil Mapdan mengumpulkan semua item dengan kunci yang diinginkan dalam array dan memetakan kunci yang diinginkan dengan mengambil elemen yang diayak dari grup yang diinginkan.

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
sumber
👏That ini fav saya, saya melakukan hal yang sama namun menggunakan {}bukannya 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;
})

Solusi ini akan menambahkan objek di akhir jika kunci pengurutan tidak ada dalam array referensi

JD-V
sumber
0

ini seharusnya bekerja:

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
sumber
0

Anda dapat mencoba metode ini.

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
sumber
0

ES6

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

Lebih banyak contoh dengan berbagai input array

Bisakah Rau
sumber
0

Jika Anda tiba di sini perlu melakukan ini dengan berbagai objek, berikut adalah adaptasi dari jawaban luar biasa @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);
})
pengguna2521295
sumber
-1

Gunakan metode $ .inArray () dari jQuery. Anda kemudian dapat melakukan sesuatu seperti ini

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]);
}
racun 20
sumber
-1

Gunakan persimpangan dua array.

Ex:

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

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

_.intersection(sortArray, arrayToBeSort) 

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

jika 'z dan' s 'di luar jangkauan array pertama, tambahkan di akhir hasil

Joe.CK
sumber
-4

Anda dapat melakukan sesuatu seperti ini:

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

Anda dapat mengujinya di sini .

Catatan: ini mengasumsikan array yang Anda berikan berukuran sama, Anda perlu menambahkan beberapa pemeriksaan tambahan jika ini tidak terjadi.

lihat tautan

lihat

Gadde
sumber
Ini sangat perlu diedit. tidak hanya jfiddle mengembalikan hasil yang salah, nama argumen fungsi tidak cocok dengan konten dalam?
twobob