Urutkan array objek berdasarkan nilai properti string

2763

Saya memiliki berbagai objek JavaScript:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

Bagaimana saya bisa mengurutkannya berdasarkan nilai last_nomdalam JavaScript?

Saya tahu sort(a,b), tapi itu hanya bekerja pada string dan angka. Apakah saya perlu menambahkan toString()metode ke objek saya?

Tyrone Slothrop
sumber
7
Script ini memungkinkan Anda untuk melakukan hal itu kecuali jika Anda ingin menulis fungsi atau penyortir perbandingan Anda sendiri: thomasfrank.se/sorting_things.html
Baversjo
cara tercepat adalah dengan menggunakan modul array- isomorfik yang bekerja secara native di kedua browser dan node, mendukung semua jenis input, bidang yang dikomputasi dan pesanan penyortiran khusus.
Lloyd

Jawaban:

3923

Cukup mudah untuk menulis fungsi perbandingan Anda sendiri:

function compare( a, b ) {
  if ( a.last_nom < b.last_nom ){
    return -1;
  }
  if ( a.last_nom > b.last_nom ){
    return 1;
  }
  return 0;
}

objs.sort( compare );

Atau sebaris (c / o Marco Demaio):

objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0)); 
Wogan
sumber
441
Atau sebaris: objs.sort (fungsi (a, b) {return (a.last_nom> b.last_nom)? 1: ((b.last_nom> a.last_nom)? -1: 0);});
Marco Demaio
176
return a.last_nom.localeCompare(b.last_nom)akan bekerja juga.
Cerbrus
146
bagi mereka yang mencari jenis yang bidangnya numerik, badan fungsi bandingkan: return a.value - b.value;(ASC)
Andre Figueiredo
20
@Cerbrus localeComparepenting ketika menggunakan karakter beraksen dalam bahasa asing, dan lebih elegan juga.
Marcos Lima
839

Anda juga bisa membuat fungsi sortir dinamis yang mengurutkan objek berdasarkan nilainya yang Anda berikan:

function dynamicSort(property) {
    var sortOrder = 1;
    if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a,b) {
        /* next line works with strings and numbers, 
         * and you may want to customize it to your needs
         */
        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result * sortOrder;
    }
}

Jadi Anda dapat memiliki berbagai objek seperti ini:

var People = [
    {Name: "Name", Surname: "Surname"},
    {Name:"AAA", Surname:"ZZZ"},
    {Name: "Name", Surname: "AAA"}
];

... dan itu akan berfungsi ketika Anda melakukannya:

People.sort(dynamicSort("Name"));
People.sort(dynamicSort("Surname"));
People.sort(dynamicSort("-Surname"));

Sebenarnya ini sudah menjawab pertanyaan. Bagian di bawah ini ditulis karena banyak orang menghubungi saya, mengeluh bahwa itu tidak berfungsi dengan beberapa parameter .

Beberapa parameter

Anda dapat menggunakan fungsi di bawah ini untuk menghasilkan fungsi sortir dengan beberapa parameter sortir.

function dynamicSortMultiple() {
    /*
     * save the arguments object as it will be overwritten
     * note that arguments object is an array-like object
     * consisting of the names of the properties to sort by
     */
    var props = arguments;
    return function (obj1, obj2) {
        var i = 0, result = 0, numberOfProperties = props.length;
        /* try getting a different result from 0 (equal)
         * as long as we have extra properties to compare
         */
        while(result === 0 && i < numberOfProperties) {
            result = dynamicSort(props[i])(obj1, obj2);
            i++;
        }
        return result;
    }
}

Yang memungkinkan Anda melakukan hal seperti ini:

People.sort(dynamicSortMultiple("Name", "-Surname"));

Array subkelas

Untuk yang beruntung di antara kita yang dapat menggunakan ES6, yang memungkinkan memperluas objek asli:

class MyArray extends Array {
    sortBy(...args) {
        return this.sort(dynamicSortMultiple.apply(null, args));
    }
}

Itu akan memungkinkan ini:

MyArray.from(People).sortBy("Name", "-Surname");
Ege Özcan
sumber
Harap perhatikan bahwa nama properti dalam JavaScript dapat berupa string apa pun dan jika Anda memiliki properti yang dimulai dengan "-" (sangat tidak mungkin dan mungkin bukan ide yang baik), Anda harus memodifikasi fungsi dynamicSort untuk menggunakan sesuatu yang lain sebagai pembalikan. indikator.
Ege Özcan
Saya perhatikan bahwa dynamicSort()pada contoh di atas akan menempatkan huruf besar di depan huruf kecil. Sebagai contoh, jika saya memiliki nilai-nilai APd, Aklindan Abe- hasil dalam ASC semacam harus Abe, Aklin, APd. Tapi dengan contoh Anda, hasilnya APd, Abe, Aklin. Pokoknya untuk memperbaiki perilaku ini?
Lloyd Banks
6
@LloydBanks jika Anda menggunakan ini secara ketat untuk string, maka Anda dapat menggunakan var result = a[property].localeCompare(b[property]);bukan var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;.
Ege Özcan
1
@ EgeÖzcan Seharusnya meletakkan item di atas atau bawah, ini implementasi yang akhirnya saya gunakan pastebin.com/g4dt23jU
dzh
1
Ini adalah solusi hebat tetapi memiliki satu masalah jika Anda membandingkan angka. Silakan tambahkan ini sebelum cek:if( !isNaN(a[property]) ) a[property] = Number(a[property]); if( !isNaN(b[property]) ) b[property] = Number(b[property]);
Ivijan Stefan Stipić
416

Di ES6 / ES2015 atau yang lebih baru, Anda dapat melakukan ini:

objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom));

Sebelum ke ES6 / ES2015

objs.sort(function(a, b) {
    return a.last_nom.localeCompare(b.last_nom)
});
Vlad Bezden
sumber
29
ini telah tersedia sejak JS 1.1, potongan panah gemuk untuk ini adalah bagian ES6 / 2015. Tapi tetap sangat berguna, dan jawaban terbaik menurut saya
Jon Harding
13
@Ratkelwalkar: jika Anda perlu membalikkan itu hanya beralih perbandingan a dan b: objs.sort ((a, b) => b.last_nom.localeCompare (a.last_nom));
Vlad Bezden
apakah mungkin untuk menggunakan indeks juga untuk mengatasi lapangan untuk menyortir: bukan last_nommenggunakan hanya nomor dalam array: 1?
dan-bri
4
@VladBezden terima kasih atas jawaban Anda! Solusi ini adalah yang pertama dengan upaya terprogram sangat kecil dan hasil pengurutan yang benar dan array string seperti: ["Name1", "Name10", "Name2", "sesuatu yang lain", "Name11"]. Saya mulai menyortir untuk bekerja dengan benarobjs.sort((a, b) => a.last_nom.localeCompare(b.last_nom, undefined, {numberic: true}));
scipper
1
Untuk melakukan ini dengan angka menurun: .sort ((a, b) => b.numberProperty - a.numberProperty). Ascending: .sort ((a, b) => a.numberProperty - b.numberProperty)
Sebastian Patten
188

underscore.js

gunakan garis bawah, kecil dan mengagumkan ...

sortBy_.sortBy (daftar, iterator, [konteks]) Mengembalikan salinan daftar yang diurutkan, diberi peringkat dalam urutan naik dengan hasil menjalankan setiap nilai melalui iterator. Iterator juga bisa berupa nama string properti yang akan disortir berdasarkan (mis. Panjang).

var objs = [ 
  { first_nom: 'Lazslo',last_nom: 'Jamf' },
  { first_nom: 'Pig', last_nom: 'Bodine'  },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];

var sortedObjs = _.sortBy( objs, 'first_nom' );
David Morrow
sumber
18
David, bisakah Anda mengedit jawaban untuk mengatakan var sortedObjs = _.sortBy( objs, 'first_nom' );,. objsakan tidak diurutkan dirinya sebagai hasil dari ini. Fungsi akan mengembalikan array yang diurutkan. Itu akan membuatnya lebih eksplisit.
Jess
10
Untuk membalikkan sortir:var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse();
Erdal G.
2
Anda perlu memuat libary javascript "garis bawah":<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"> </script>
dan-bri
5
Juga tersedia Lodashbagi mereka yang lebih suka yang
WoJ
2
Di lodash ini akan sama: var sortedObjs = _.sortBy( objs, 'first_nom' );atau jika Anda menginginkannya dalam urutan yang berbeda:var sortedObjs = _.orderBy( objs, ['first_nom'],['dsc'] );
Travis Heeter
186

Jangan mengerti mengapa orang membuatnya begitu rumit:

objs.sort(function(a, b){
  return a.last_nom > b.last_nom;
});

Untuk mesin yang lebih ketat:

objs.sort(function(a, b){
  return a.last_nom == b.last_nom ? 0 : +(a.last_nom > b.last_nom) || -1;
});

Tukar operator untuk mengaturnya berdasarkan urutan abjad terbalik.

p3lim
sumber
17
Ini sebenarnya tidak benar karena fungsi yang digunakan dalam sort harus mengembalikan -1, 0, atau 1 tetapi fungsi di atas mengembalikan boolean. Penyortiran berfungsi dengan baik di chrome tetapi gagal di PhantomJS misalnya. Lihat code.google.com/p/phantomjs/issues/detail?id=1090
schup
Beberapa mesin menyebabkan kebodohan, cara ini agak mengeksploitasi itu. Saya telah memperbarui balasan saya dengan versi yang tepat.
p3lim
19
Saya sarankan mengedit untuk mengeluarkan versi pertama. Lebih ringkas sehingga terlihat lebih menarik, tetapi tidak berfungsi, setidaknya tidak andal. Jika seseorang mencobanya di satu browser dan berfungsi, mereka mungkin tidak menyadari bahwa mereka memiliki masalah (terutama jika mereka belum membaca komentar). Versi kedua berfungsi dengan baik sehingga benar-benar tidak perlu untuk yang pertama.
Kate
2
@Simon Saya sangat menghargai memiliki versi "sedikit salah" pertama, karena implementasi yang lebih ketat membutuhkan beberapa detik untuk menguraikan dan memahami dan akan jauh lebih sulit tanpanya.
Aaron Sherman
1
@ Lion789 lakukan saja ini:if(a.count == b.count) return a.name > b.name; else return a.count > b.count;
p3lim
62

Jika Anda memiliki nama belakang yang sama, Anda bisa mengurutkannya berdasarkan nama depan-

obj.sort(function(a,b){
  if(a.last_nom< b.last_nom) return -1;
  if(a.last_nom >b.last_nom) return 1;
  if(a.first_nom< b.first_nom) return -1;
  if(a.first_nom >b.first_nom) return 1;
  return 0;
});
kennebec
sumber
@BadFeelingAboutThis apa artinya mengembalikan -1 atau 1? Saya mengerti bahwa -1 secara harfiah berarti bahwa A kurang dari B hanya dengan sintaks, tetapi mengapa menggunakan 1 atau -1? Saya melihat semua orang menggunakan angka-angka itu sebagai nilai pengembalian, tetapi mengapa? Terima kasih.
Chris22
1
@ Chris22 angka negatif yang dikembalikan berarti bharus muncul setelah adalam array. Jika angka positif dikembalikan, itu berarti aharus datang setelah b. Jika 0dikembalikan, itu berarti mereka dianggap sama. Anda selalu dapat membaca dokumentasi: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
BadFeelingAboutThis
@BadFeelingAboutThis terima kasih, untuk penjelasan dan tautannya. Percaya atau tidak, saya meng-google berbagai potongan kode menggunakan 1, 0, -1sebelum saya bertanya di sini. Saya hanya tidak menemukan info yang saya butuhkan.
Chris22
48

Solusi sederhana dan cepat untuk masalah ini menggunakan pewarisan prototipe:

Array.prototype.sortBy = function(p) {
  return this.slice(0).sort(function(a,b) {
    return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;
  });
}

Contoh / Penggunaan

objs = [{age:44,name:'vinay'},{age:24,name:'deepak'},{age:74,name:'suresh'}];

objs.sortBy('age');
// Returns
// [{"age":24,"name":"deepak"},{"age":44,"name":"vinay"},{"age":74,"name":"suresh"}]

objs.sortBy('name');
// Returns
// [{"age":24,"name":"deepak"},{"age":74,"name":"suresh"},{"age":44,"name":"vinay"}]

Pembaruan: Tidak lagi memodifikasi array asli.

Vinay Aggarwal
sumber
5
Itu tidak hanya mengembalikan array lain. tapi sebenarnya macam yang asli !.
Vinay Aggarwal
Jika Anda ingin memastikan Anda menggunakan pengurutan alami dengan angka (yaitu, 0,1,2,10,11 dll ...) gunakan parseInt dengan set Radix. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… so: return (parseInt (a [p], 10)> parseInt (b [p], 10))? 1: (parseInt (a [p], 10) <parseInt (b [p], 10))? -1: 0;
Paul
@codehuntr Terima kasih telah memperbaikinya. tetapi saya kira daripada membuat fungsi sortir untuk melakukan sensitisasi ini, lebih baik jika kita membuat fungsi terpisah untuk memperbaiki tipe data. Karena fungsi sortir tidak bisa memastikan properti mana yang akan berisi data seperti apa. :)
Vinay Aggarwal
Sangat bagus. Balikkan panah untuk mendapatkan efek asc / desc.
Abdul Sadik Yalcin
4
tidak pernah, pernah menyarankan solusi yang memodifikasi prototipe objek inti
pbanka
39

Pada 2018 ada solusi yang jauh lebih pendek dan elegan. Gunakan saja. Array.prototype.sort () .

Contoh:

var items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: 'Magnetic', value: 13 },
  { name: 'Zeros', value: 37 }
];

// sort by value
items.sort(function (a, b) {
  return a.value - b.value;
});
0leg
sumber
7
Dalam string pertanyaan digunakan untuk perbandingan sebagai lawan dari angka. Jawaban Anda sangat bagus untuk mengurutkan berdasarkan angka, tetapi tidak begitu baik untuk perbandingan berdasarkan string.
smcstewart
The a.value - b.valuedigunakan untuk membandingkan atribut objek ( nomor dalam kasus ini) dapat diadopsi untuk berbagai waktu data. Misalnya, regex dapat digunakan untuk membandingkan setiap pasangan dari string yang berdekatan .
0leg
1
@ 0leg Saya ingin melihat contoh di sini menggunakan regex untuk membandingkan string dengan cara ini.
Bob Stein
Implementasi ini cukup baik jika Anda perlu mengurutkannya berdasarkan ID. Ya, Anda telah menyarankan untuk menggunakan regex untuk membandingkan string tetangga yang membuat solusi lebih rumit sedangkan tujuan dari versi disederhanakan ini akan sebaliknya jika regex digunakan bersama dengan solusi yang diberikan. Kesederhanaan adalah yang terbaik.
surendrapanday
33

Jawaban lama yang tidak benar:

arr.sort((a, b) => a.name > b.name)

MEMPERBARUI

Dari komentar Beauchamp:

arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))

Format yang lebih mudah dibaca:

arr.sort((a, b) => {
  if (a.name < b.name) return -1
  return a.name > b.name ? 1 : 0
})

Tanpa terary bersarang:

arr.sort((a, b) => a.name < b.name ? - 1 : Number(a.name > b.name))

Penjelasan: Number()akan dilemparkan trueke 1dan falseke0 .

Damjan Pavlica
sumber
Berhasil
@ AO17 tidak, itu tidak akan. Anda tidak dapat mengurangi string.
Patrick Roberts
15
Ini harus dilakukan:arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))
Jean-François Beauchamp
3
@ Jean-FrançoisBeauchamp, solusi Anda berfungsi dengan sangat baik dan jauh lebih baik.
Ahsan
yang ke-3 dengan Angka itu langsung dan menyenangkan!
Kojo
29

Alih-alih menggunakan fungsi perbandingan kustom, Anda juga bisa membuat tipe objek dengan toString()metode kustom (yang dipanggil oleh fungsi perbandingan default):

function Person(firstName, lastName) {
    this.firtName = firstName;
    this.lastName = lastName;
}

Person.prototype.toString = function() {
    return this.lastName + ', ' + this.firstName;
}

var persons = [ new Person('Lazslo', 'Jamf'), ...]
persons.sort();
Christoph
sumber
29

Lodash.js (superset dari Underscore.js )

Adalah baik untuk tidak menambahkan kerangka kerja untuk setiap bagian sederhana dari logika, tetapi mengandalkan kerangka kerja utilitas yang teruji dapat mempercepat pengembangan dan mengurangi jumlah bug.

Lodash menghasilkan kode yang sangat bersih dan mempromosikan pemrograman yang lebih fungsional gaya . Dalam sekejap menjadi jelas apa maksud dari kode tersebut.

Masalah OP dapat dengan mudah dipecahkan sebagai:

const sortedObjs = _.sortBy(objs, 'last_nom');

Info lebih lanjut? Misalnya kita memiliki objek bersarang berikut:

const users = [
  { 'user': {'name':'fred', 'age': 48}},
  { 'user': {'name':'barney', 'age': 36 }},
  { 'user': {'name':'wilma'}},
  { 'user': {'name':'betty', 'age': 32}}
];

Kita sekarang dapat menggunakan singkatan _.propertyuser.age untuk menentukan jalur ke properti yang harus dicocokkan. Kami akan mengurutkan objek pengguna berdasarkan properti umur bersarang. Ya, ini memungkinkan pencocokan properti bersarang!

const sortedObjs = _.sortBy(users, ['user.age']);

Ingin dibalik? Tidak masalah. Gunakan _.reverse .

const sortedObjs = _.reverse(_.sortBy(users, ['user.age']));

Ingin menggabungkan keduanya menggunakan rantai ?

const { chain } = require('lodash');
const sortedObjs = chain(users).sortBy('user.age').reverse().value();

Atau kapan Anda lebih suka mengalir dari rantai

const { flow, reverse, sortBy } = require('lodash/fp');
const sortedObjs = flow([sortBy('user.age'), reverse])(users); 
Nico Van Belle
sumber
28

Kamu bisa menggunakan

Cara termudah: Lodash

( https://lodash.com/docs/4.17.10#orderBy )

Metode ini seperti _.sortBy kecuali bahwa itu memungkinkan menentukan urutan perintah dari iterate untuk mengurutkan berdasarkan. Jika pesanan tidak ditentukan, semua nilai diurutkan dalam urutan naik. Kalau tidak, tentukan urutan "desc" untuk turun atau "asc" untuk urutan urutan naik dari nilai yang sesuai.

Argumen

koleksi (Array | Objek): Koleksi untuk beralih. [iteratees = [_. identity]] (Array [] | Function [] | Object [] | string []): The iteratees untuk mengurutkan berdasarkan. [order] (string []): Urutan pesanan iteratees.

Kembali

(Array): Mengembalikan array yang diurutkan baru.


var _ = require('lodash');
var homes = [
    {"h_id":"3",
     "city":"Dallas",
     "state":"TX",
     "zip":"75201",
     "price":"162500"},
    {"h_id":"4",
     "city":"Bevery Hills",
     "state":"CA",
     "zip":"90210",
     "price":"319250"},
    {"h_id":"6",
     "city":"Dallas",
     "state":"TX",
     "zip":"75000",
     "price":"556699"},
    {"h_id":"5",
     "city":"New York",
     "state":"NY",
     "zip":"00010",
     "price":"962500"}
    ];

_.orderBy(homes, ['city', 'state', 'zip'], ['asc', 'desc', 'asc']);
Harvey
sumber
23

Ada banyak jawaban bagus di sini, tetapi saya ingin menunjukkan bahwa mereka dapat diperluas sangat sederhana untuk mencapai penyortiran yang jauh lebih kompleks. Satu-satunya hal yang harus Anda lakukan adalah menggunakan operator OR untuk rantai fungsi perbandingan seperti ini:

objs.sort((a,b)=> fn1(a,b) || fn2(a,b) || fn3(a,b) )

Di mana fn1,, fn2... adalah fungsi sortir yang mengembalikan [-1,0,1]. Ini menghasilkan "sorting by fn1", "sorting by fn2" yang hampir sama dengan ORDER BY dalam SQL.

Solusi ini didasarkan pada perilaku ||operator yang mengevaluasi ekspresi pertama yang dievaluasi yang dapat dikonversi menjadi true .

Bentuk paling sederhana hanya memiliki satu fungsi inline seperti ini:

// ORDER BY last_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) )

Dengan dua langkah last_nom, first_nomurutan akan terlihat seperti ini:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) || 
                  a.first_nom.localeCompare(b.first_nom)  )

Fungsi perbandingan generik bisa jadi seperti ini:

// ORDER BY <n>
let cmp = (a,b,n)=>a[n].localeCompare(b[n])

Fungsi ini dapat diperluas untuk mendukung bidang angka, sensitivitas huruf, tipe data arbiter, dll.

Anda dapat mereka menggunakannya dengan merantai mereka dengan mengurutkan prioritas:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> cmp(a,b, "last_nom") || cmp(a,b, "first_nom") )
// ORDER_BY last_nom, first_nom DESC
objs.sort((a,b)=> cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )
// ORDER_BY last_nom DESC, first_nom DESC
objs.sort((a,b)=> -cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )

Intinya di sini adalah bahwa JavaScript murni dengan pendekatan fungsional dapat membawa Anda jauh tanpa perpustakaan eksternal atau kode kompleks. Ini juga sangat efektif, karena tidak ada parsing string yang harus dilakukan

Tero Tolonen
sumber
23

Contoh penggunaan:

objs.sort(sortBy('last_nom'));

Naskah:

/**
 * @description
 * Returns a function which will sort an
 * array of objects by the given key.
 *
 * @param  {String}  key
 * @param  {Boolean} reverse
 * @return {Function}
 */
const sortBy = (key, reverse) => {

  // Move smaller items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  const moveSmaller = reverse ? 1 : -1;

  // Move larger items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  const moveLarger = reverse ? -1 : 1;

  /**
   * @param  {*} a
   * @param  {*} b
   * @return {Number}
   */
  return (a, b) => {
    if (a[key] < b[key]) {
      return moveSmaller;
    }
    if (a[key] > b[key]) {
      return moveLarger;
    }
    return 0;
  };
};
Jamie Mason
sumber
terima kasih telah memecah ini, saya mencoba untuk memahami mengapa angka 1, 0, -1digunakan untuk pemesanan semacam. Bahkan dengan penjelasan Anda di atas, yang terlihat sangat bagus - saya masih belum mengerti. Saya selalu berpikir -1ketika menggunakan properti panjang array, yaitu: arr.length = -1berarti item tidak ditemukan. Saya mungkin mencampuradukkan hal-hal di sini, tetapi bisakah Anda membantu saya memahami mengapa angka 1, 0, -1digunakan untuk menentukan urutan? Terima kasih.
Chris22
1
Ini tidak sepenuhnya akurat tetapi mungkin membantu untuk memikirkannya seperti ini: fungsi dilewatkan ke array.sort dipanggil sekali untuk setiap item dalam array, sebagai argumen bernama "a". Nilai kembali dari setiap panggilan fungsi adalah bagaimana indeks (nomor posisi saat ini) dari item "a" harus diubah dibandingkan dengan item berikutnya "b". Indeks menentukan urutan array (0, 1, 2 dll) Jadi jika "a" berada di indeks 5 dan Anda mengembalikan -1 lalu 5 + -1 == 4 (pindahkan lebih dekat ke depan) 5 + 0 == 5 (simpan di tempat itu) dll. Berjalan array membandingkan 2 tetangga setiap kali sampai mencapai akhir, meninggalkan array diurutkan.
Jamie Mason
terima kasih telah meluangkan waktu untuk menjelaskan ini lebih lanjut. Jadi dengan menggunakan penjelasan Anda dan MDN Array.prototype.sort , saya akan memberi tahu Anda apa yang saya pikirkan tentang ini: dibandingkan dengan adan b, jika alebih besar dari btambahkan 1 ke indeks adan letakkan di belakang b, jika akurang dari b, kurangi 1 dari adan letakkan di depan b. Jika adan bsama, tambahkan 0 ke adan biarkan di tempatnya.
Chris22
22

Saya belum melihat pendekatan khusus ini disarankan, jadi inilah metode perbandingan singkat yang saya suka gunakan yang bekerja untuk keduanya stringdan number:

const objs = [ 
  { first_nom: 'Lazslo', last_nom: 'Jamf'     },
  { first_nom: 'Pig',    last_nom: 'Bodine'   },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];

const sortBy = fn => (a, b) => {
  const fa = fn(a);
  const fb = fn(b);
  return -(fa < fb) || +(fa > fb);
};

const getLastName = o => o.last_nom;
const sortByLastName = sortBy(getLastName);

objs.sort(sortByLastName);
console.log(objs.map(getLastName));

Berikut penjelasan tentang sortBy():

sortBy()menerima a fnyang memilih nilai apa dari objek untuk digunakan sebagai perbandingan, dan mengembalikan fungsi yang dapat diteruskan langsung ke Array.prototype.sort(). Dalam contoh ini, kami menggunakan o.last_nomsebagai nilai untuk perbandingan, jadi setiap kali kami menerima dua objek melalui Array.prototype.sort()seperti

{ first_nom: 'Lazslo', last_nom: 'Jamf' }

dan

{ first_nom: 'Pig', last_nom: 'Bodine' }

kita gunakan

(a, b) => {
  const fa = fn(a);
  const fb = fn(b);
  return -(fa < fb) || +(fa > fb);
};

untuk membandingkannya.

Mengingat itu fn = o => o.last_nom, kita dapat memperluas fungsi bandingkan ke yang setara

(a, b) => {
  const fa = a.last_nom;
  const fb = b.last_nom;
  return -(fa < fb) || +(fa > fb);
};

OR logis || memiliki fungsi hubungan arus pendek yang sangat berguna di sini. Karena cara kerjanya, fungsi tubuh di atas berarti

if (fa < fb) return -1;
if (fa > fb) return 1;
return 0;

Sebagai bonus tambahan, inilah yang setara di ECMAScript 5 tanpa fungsi panah, yang sayangnya lebih bertele-tele:

var objs = [ 
  { first_nom: 'Lazslo', last_nom: 'Jamf'     },
  { first_nom: 'Pig',    last_nom: 'Bodine'   },
  { first_nom: 'Pirate', last_nom: 'Prentice' }
];

var sortBy = function (fn) {
  return function (a, b) {
    var fa = fn(a);
    var fb = fn(b);
    return -(fa < fb) || +(fa > fb);
  };
};

var getLastName = function (o) { return o.last_nom };
var sortByLastName = sortBy(getLastName);

objs.sort(sortByLastName);
console.log(objs.map(getLastName));

Patrick Roberts
sumber
17

Saya tahu pertanyaan ini terlalu lama, tetapi saya tidak melihat implementasi yang serupa dengan saya.
Versi ini didasarkan pada idiom transformasi Schwartzian .

function sortByAttribute(array, ...attrs) {
  // generate an array of predicate-objects contains
  // property getter, and descending indicator
  let predicates = attrs.map(pred => {
    let descending = pred.charAt(0) === '-' ? -1 : 1;
    pred = pred.replace(/^-/, '');
    return {
      getter: o => o[pred],
      descend: descending
    };
  });
  // schwartzian transform idiom implementation. aka: "decorate-sort-undecorate"
  return array.map(item => {
    return {
      src: item,
      compareValues: predicates.map(predicate => predicate.getter(item))
    };
  })
  .sort((o1, o2) => {
    let i = -1, result = 0;
    while (++i < predicates.length) {
      if (o1.compareValues[i] < o2.compareValues[i]) result = -1;
      if (o1.compareValues[i] > o2.compareValues[i]) result = 1;
      if (result *= predicates[i].descend) break;
    }
    return result;
  })
  .map(item => item.src);
}

Berikut ini contoh cara menggunakannya:

let games = [
  { name: 'Mashraki',          rating: 4.21 },
  { name: 'Hill Climb Racing', rating: 3.88 },
  { name: 'Angry Birds Space', rating: 3.88 },
  { name: 'Badland',           rating: 4.33 }
];

// sort by one attribute
console.log(sortByAttribute(games, 'name'));
// sort by mupltiple attributes
console.log(sortByAttribute(games, '-rating', 'name'));
a8m
sumber
14

Menyortir (lebih banyak) Susunan Kompleks Objek

Karena Anda mungkin menemukan struktur data yang lebih kompleks seperti array ini, saya akan memperluas solusinya.

TL; DR

Adalah versi yang lebih pluggable berdasarkan jawaban yang sangat bagus dari @ ege-Özcan .

Masalah

Saya menemukan di bawah dan tidak bisa mengubahnya. Saya juga tidak ingin meratakan objek untuk sementara waktu. Saya juga tidak ingin menggunakan garis bawah / lodash, terutama karena alasan kinerja dan kesenangan untuk mengimplementasikannya sendiri.

var People = [
   {Name: {name: "Name", surname: "Surname"}, Middlename: "JJ"},
   {Name: {name: "AAA", surname: "ZZZ"}, Middlename:"Abrams"},
   {Name: {name: "Name", surname: "AAA"}, Middlename: "Wars"}
];

Tujuan

Tujuannya adalah untuk mengurutkannya berdasarkan People.Name.name dan olehPeople.Name.surname

Rintangan

Sekarang, dalam solusi dasar menggunakan notasi braket untuk menghitung properti untuk mengurutkan secara dinamis. Namun, di sini, kita harus membuat notasi braket secara dinamis, karena Anda akan mengharapkannyaPeople['Name.name'] berfungsi - yang tidak.

Cukup melakukan People['Name']['name'], di sisi lain, bersifat statis dan hanya memungkinkan Anda untuk turun n ke tingkat .

Larutan

Penambahan utama di sini adalah berjalan menyusuri objek pohon dan menentukan nilai daun terakhir, Anda harus menentukan, serta setiap daun perantara.

var People = [
   {Name: {name: "Name", surname: "Surname"}, Middlename: "JJ"},
   {Name: {name: "AAA", surname: "ZZZ"}, Middlename:"Abrams"},
   {Name: {name: "Name", surname: "AAA"}, Middlename: "Wars"}
];

People.sort(dynamicMultiSort(['Name','name'], ['Name', '-surname']));
// Results in...
// [ { Name: { name: 'AAA', surname: 'ZZZ' }, Middlename: 'Abrams' },
//   { Name: { name: 'Name', surname: 'Surname' }, Middlename: 'JJ' },
//   { Name: { name: 'Name', surname: 'AAA' }, Middlename: 'Wars' } ]

// same logic as above, but strong deviation for dynamic properties 
function dynamicSort(properties) {
  var sortOrder = 1;
  // determine sort order by checking sign of last element of array
  if(properties[properties.length - 1][0] === "-") {
    sortOrder = -1;
    // Chop off sign
    properties[properties.length - 1] = properties[properties.length - 1].substr(1);
  }
  return function (a,b) {
    propertyOfA = recurseObjProp(a, properties)
    propertyOfB = recurseObjProp(b, properties)
    var result = (propertyOfA < propertyOfB) ? -1 : (propertyOfA > propertyOfB) ? 1 : 0;
    return result * sortOrder;
  };
}

/**
 * Takes an object and recurses down the tree to a target leaf and returns it value
 * @param  {Object} root - Object to be traversed.
 * @param  {Array} leafs - Array of downwards traversal. To access the value: {parent:{ child: 'value'}} -> ['parent','child']
 * @param  {Number} index - Must not be set, since it is implicit.
 * @return {String|Number}       The property, which is to be compared by sort.
 */
function recurseObjProp(root, leafs, index) {
  index ? index : index = 0
  var upper = root
  // walk down one level
  lower = upper[leafs[index]]
  // Check if last leaf has been hit by having gone one step too far.
  // If so, return result from last step.
  if (!lower) {
    return upper
  }
  // Else: recurse!
  index++
  // HINT: Bug was here, for not explicitly returning function
  // https://stackoverflow.com/a/17528613/3580261
  return recurseObjProp(lower, leafs, index)
}

/**
 * Multi-sort your array by a set of properties
 * @param {...Array} Arrays to access values in the form of: {parent:{ child: 'value'}} -> ['parent','child']
 * @return {Number} Number - number for sort algorithm
 */
function dynamicMultiSort() {
  var args = Array.prototype.slice.call(arguments); // slight deviation to base

  return function (a, b) {
    var i = 0, result = 0, numberOfProperties = args.length;
    // REVIEW: slightly verbose; maybe no way around because of `.sort`-'s nature
    // Consider: `.forEach()`
    while(result === 0 && i < numberOfProperties) {
      result = dynamicSort(args[i])(a, b);
      i++;
    }
    return result;
  }
}

Contoh

Contoh kerja di JSBin

eljefedelrodeodeljefe
sumber
2
Mengapa? Ini bukan jawaban untuk pertanyaan awal dan "tujuan" dapat diselesaikan hanya dengan People.sort ((a, b) => {return a.Name.name.localeCompare (b.Name.name) || a.Name .surname.localeCompare (b.Name.surname)})
Tero Tolonen
12

Satu opsi lagi:

var someArray = [...];

function generateSortFn(prop, reverse) {
    return function (a, b) {
        if (a[prop] < b[prop]) return reverse ? 1 : -1;
        if (a[prop] > b[prop]) return reverse ? -1 : 1;
        return 0;
    };
}

someArray.sort(generateSortFn('name', true));

macam naik secara default.

Ravshan Samandarov
sumber
1
Versi yang sedikit berubah untuk mengurutkan berdasarkan beberapa bidang ada di sini jika diperlukan: stackoverflow.com/questions/6913512/…
Ravshan Samandarov
sepertinya itu bisa menjadi yang berikutnya: fungsi ekspor menghasilkanSortFn (prop: string, reverse: boolean = false): (... args: any) => number {return (a, b) => {return a [prop ] <b [prop] balik ? 1: -1: a [penyangga]> b [penyangga]? balik ? -1: 1: 0; }; }
Den Kerny
Saya lebih suka kode yang lebih mudah dibaca daripada yang terpendek.
Ravshan Samandarov
setuju, tetapi dalam beberapa kasus saya tidak perlu melihat fungsi utilitas.
Den Kerny
11

Fungsi sederhana yang mengurutkan array objek berdasarkan properti

function sortArray(array, property, direction) {
    direction = direction || 1;
    array.sort(function compare(a, b) {
        let comparison = 0;
        if (a[property] > b[property]) {
            comparison = 1 * direction;
        } else if (a[property] < b[property]) {
            comparison = -1 * direction;
        }
        return comparison;
    });
    return array; // Chainable
}

Pemakaian:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

sortArray(objs, "last_nom"); // Asc
sortArray(objs, "last_nom", -1); // Desc
Francois Girard
sumber
Solusi ini bekerja dengan sempurna bagi saya untuk memilah dua arah. Terima kasih
manjuvreddy
10

Cara sederhana:

objs.sort(function(a,b) {
  return b.last_nom.toLowerCase() < a.last_nom.toLowerCase();
});

Lihat itu '.toLowerCase()'perlu untuk mencegah erro dalam membandingkan string.

Caio Ladislau
sumber
4
Anda dapat menggunakan fungsi panah untuk membiarkan kode sedikit lebih elegan:objs.sort( (a,b) => b.last_nom.toLowerCase() < a.last_nom.toLowerCase() );
Sertage
Ini salah karena alasan yang sama seperti yang dijelaskan di sini .
Patrick Roberts
2
Fungsi panah tidak layak ES5. Banyak mesin masih terbatas pada ES5. Dalam kasus saya, saya menemukan jawaban di atas secara signifikan lebih baik karena saya menggunakan mesin ES5 (dipaksakan oleh perusahaan saya)
dylanh724
9

tambahan desc params untuk kode Ege Özcan

function dynamicSort(property, desc) {
    if (desc) {
        return function (a, b) {
            return (a[property] > b[property]) ? -1 : (a[property] < b[property]) ? 1 : 0;
        }   
    }
    return function (a, b) {
        return (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
    }
}
Behnam Shomali
sumber
9

Menggabungkan solusi dinamis Ege dengan ide Vinay, Anda mendapatkan solusi tangguh yang bagus:

Array.prototype.sortBy = function() {
    function _sortByAttr(attr) {
        var sortOrder = 1;
        if (attr[0] == "-") {
            sortOrder = -1;
            attr = attr.substr(1);
        }
        return function(a, b) {
            var result = (a[attr] < b[attr]) ? -1 : (a[attr] > b[attr]) ? 1 : 0;
            return result * sortOrder;
        }
    }
    function _getSortFunc() {
        if (arguments.length == 0) {
            throw "Zero length arguments not allowed for Array.sortBy()";
        }
        var args = arguments;
        return function(a, b) {
            for (var result = 0, i = 0; result == 0 && i < args.length; i++) {
                result = _sortByAttr(args[i])(a, b);
            }
            return result;
        }
    }
    return this.sort(_getSortFunc.apply(null, arguments));
}

Pemakaian:

// Utility for printing objects
Array.prototype.print = function(title) {
    console.log("************************************************************************");
    console.log("**** "+title);
    console.log("************************************************************************");
    for (var i = 0; i < this.length; i++) {
        console.log("Name: "+this[i].FirstName, this[i].LastName, "Age: "+this[i].Age);
    }
}

// Setup sample data
var arrObj = [
    {FirstName: "Zach", LastName: "Emergency", Age: 35},
    {FirstName: "Nancy", LastName: "Nurse", Age: 27},
    {FirstName: "Ethel", LastName: "Emergency", Age: 42},
    {FirstName: "Nina", LastName: "Nurse", Age: 48},
    {FirstName: "Anthony", LastName: "Emergency", Age: 44},
    {FirstName: "Nina", LastName: "Nurse", Age: 32},
    {FirstName: "Ed", LastName: "Emergency", Age: 28},
    {FirstName: "Peter", LastName: "Physician", Age: 58},
    {FirstName: "Al", LastName: "Emergency", Age: 51},
    {FirstName: "Ruth", LastName: "Registration", Age: 62},
    {FirstName: "Ed", LastName: "Emergency", Age: 38},
    {FirstName: "Tammy", LastName: "Triage", Age: 29},
    {FirstName: "Alan", LastName: "Emergency", Age: 60},
    {FirstName: "Nina", LastName: "Nurse", Age: 54}
];

//Unit Tests
arrObj.sortBy("LastName").print("LastName Ascending");
arrObj.sortBy("-LastName").print("LastName Descending");
arrObj.sortBy("LastName", "FirstName", "-Age").print("LastName Ascending, FirstName Ascending, Age Descending");
arrObj.sortBy("-FirstName", "Age").print("FirstName Descending, Age Ascending");
arrObj.sortBy("-Age").print("Age Descending");
Mike R
sumber
1
Terima kasih atas idenya! Ngomong-ngomong, tolong jangan mendorong orang untuk mengubah Array Prototype (lihat peringatan di akhir contoh saya).
Ege Özcan
8

Berdasarkan contoh Anda, Anda perlu mengurutkan berdasarkan dua bidang (nama belakang, nama depan), bukan satu. Anda bisa menggunakan pustaka Alasql untuk membuat ini dalam satu baris:

var res = alasql('SELECT * FROM ? ORDER BY last_nom, first_nom',[objs]);

Coba contoh ini di jsFiddle .

agershun
sumber
8
objs.sort(function(a,b){return b.last_nom>a.last_nom})
Roshni Bokade
sumber
1
Sebenarnya itu tidak berhasil, harus menggunakan jawaban yang diterima. Itu tidak menyortir dengan benar.
madprops
8

Diberikan contoh asli:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

Urutkan berdasarkan beberapa bidang:

objs.sort(function(left, right) {
    var last_nom_order = left.last_nom.localeCompare(right.last_nom);
    var first_nom_order = left.first_nom.localeCompare(right.first_nom);
    return last_nom_order || first_nom_order;
});

Catatan

  • a.localeCompare(b)adalah universal didukung dan kembali -1,0,1 jika a<b, a==b,a>b masing-masing.
  • ||di baris terakhir memberi last_nomprioritas lebihfirst_nom .
  • Pengurangan bekerja pada bidang numerik: var age_order = left.age - right.age;
  • Negasikan ke urutan terbalik, return -last_nom_order || -first_nom_order || -age_order;
Bob Stein
sumber
8

Coba ini,

UPTO ES5

//Ascending Sort
items.sort(function (a, b) {
   return a.value - b.value;
});


//Descending Sort
items.sort(function (a, b) {
   return b.value - a.value;
});


IN ES6 & above:

// Ascending sort
items.sort((a, b) => a.value - b.value);

// Descending Sort
 items.sort((a, b) => b.value - a.value);
Abhishek
sumber
solusi terbaik dan mudah
Omar Hasan
7

Anda mungkin perlu mengubahnya menjadi huruf kecil untuk mencegah kebingungan.

objs.sort(function (a,b) {

var nameA=a.last_nom.toLowerCase(), nameB=b.last_nom.toLowerCase()

if (nameA < nameB)
  return -1;
if (nameA > nameB)
  return 1;
return 0;  //no sorting

})
Burak Keceli
sumber
7
function compare(propName) {
    return function(a,b) {
        if (a[propName] < b[propName])
            return -1;
        if (a[propName] > b[propName])
            return 1;
        return 0;
    };
}

objs.sort(compare("last_nom"));
Evgenii
sumber
1
Silakan pertimbangkan untuk mengedit posting Anda untuk menambahkan lebih banyak penjelasan tentang apa yang kode Anda lakukan dan mengapa itu akan menyelesaikan masalah. Jawaban yang sebagian besar hanya berisi kode (bahkan jika itu berfungsi) biasanya tidak akan membantu OP untuk memahami masalah mereka.
Drenmi
7

Menggunakan Ramda,

npm pasang ramda

import R from 'ramda'
var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];
var ascendingSortedObjs = R.sortBy(R.prop('last_nom'), objs)
var descendingSortedObjs = R.reverse(ascendingSortedObjs)
Sridhar Sg
sumber
6

Ini adalah masalah yang sederhana, tidak tahu mengapa orang memiliki solusi yang begitu rumit.
Fungsi sortir sederhana (berdasarkan algoritma sortir cepat):

function sortObjectsArray(objectsArray, sortKey)
        {
            // Quick Sort:
            var retVal;

            if (1 < objectsArray.length)
            {
                var pivotIndex = Math.floor((objectsArray.length - 1) / 2);  // middle index
                var pivotItem = objectsArray[pivotIndex];                    // value in the middle index
                var less = [], more = [];

                objectsArray.splice(pivotIndex, 1);                          // remove the item in the pivot position
                objectsArray.forEach(function(value, index, array)
                {
                    value[sortKey] <= pivotItem[sortKey] ?                   // compare the 'sortKey' proiperty
                        less.push(value) :
                        more.push(value) ;
                });

                retVal = sortObjectsArray(less, sortKey).concat([pivotItem], sortObjectsArray(more, sortKey));
            }
            else
            {
                retVal = objectsArray;
            }

            return retVal;
        }

Gunakan contoh:

var myArr = 
        [
            { val: 'x', idx: 3 },
            { val: 'y', idx: 2 },
            { val: 'z', idx: 5 },
        ];
myArr = sortObjectsArray(myArr, 'idx');
Gil Epshtain
sumber
5
Bagaimana menerapkan quick sort in js solusi sederhana? Algoritma sederhana tetapi bukan solusi sederhana.
Andrew
Sederhana karena tidak menggunakan pustaka luar dan itu tidak mengubah prototipe objek. Menurut pendapat saya, panjang kode tidak berdampak langsung pada kompleksitas kode
Gil Epshtain
2
Baiklah, izinkan saya mencoba dengan kata-kata yang berbeda: Bagaimana menciptakan kembali roda adalah solusi sederhana?
Roberto14