Bagaimana cara mengurutkan array objek berdasarkan beberapa bidang?

148

Dari pertanyaan awal ini , bagaimana saya menerapkan semacam pada beberapa bidang?

Menggunakan struktur yang sedikit disesuaikan ini, bagaimana saya mengurutkan kota (menaik) & lalu harga (turun)?

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

Saya menyukai fakta daripada jawaban yang diberikan yang memberikan pendekatan umum. Di mana saya berencana untuk menggunakan kode ini, saya harus mengurutkan tanggal serta hal-hal lain. Kemampuan untuk "prima" objek tampak berguna, jika tidak sedikit rumit.

Saya sudah mencoba membangun jawaban ini menjadi contoh umum yang bagus, tetapi saya tidak memiliki banyak keberuntungan.

Mike
sumber
Apakah Anda ingin mencari atau mengurutkan?
Felix Kling
Apa sebenarnya masalah yang Anda hadapi dengan menggunakan jawaban kedua yang Anda tautkan?
kanon
Itu tidak cukup generik. Saya sepertinya akan menambahkan lautan kode ketika saya ingin mengatakan sort(["first-field", "ASC"], ["second-field", "DSC"]); ini lebih rumit ketika saya mencoba menambahkan logika "primer" pada jawaban pertama sehingga saya dapat menangani tanggal, ketidakpekaan huruf, dll.
Mike
Atau Anda dapat menerapkan bobot untuk setiap bidang
onmyway133
Anda dapat memeriksa lodash.com/docs/4.17.11#orderBy , jika Anda setuju menggunakan lodash
Deepanshu Arora

Jawaban:

83

Metode penyortiran multi dimensi, berdasarkan jawaban ini :

Pembaruan : Ini adalah versi "yang dioptimalkan". Itu jauh lebih preprocessing dan membuat fungsi perbandingan untuk setiap opsi penyortiran sebelumnya. Mungkin perlu lebih banyak memori (karena menyimpan fungsi untuk setiap opsi penyortiran, tetapi harus melakukan preform sedikit lebih baik karena tidak harus menentukan pengaturan yang benar selama perbandingan. Saya belum melakukan profiling sekalipun.

var sort_by;

(function() {
    // utility functions
    var default_cmp = function(a, b) {
            if (a == b) return 0;
            return a < b ? -1 : 1;
        },
        getCmpFunc = function(primer, reverse) {
            var dfc = default_cmp, // closer in scope
                cmp = default_cmp;
            if (primer) {
                cmp = function(a, b) {
                    return dfc(primer(a), primer(b));
                };
            }
            if (reverse) {
                return function(a, b) {
                    return -1 * cmp(a, b);
                };
            }
            return cmp;
        };

    // actual implementation
    sort_by = function() {
        var fields = [],
            n_fields = arguments.length,
            field, name, reverse, cmp;

        // preprocess sorting options
        for (var i = 0; i < n_fields; i++) {
            field = arguments[i];
            if (typeof field === 'string') {
                name = field;
                cmp = default_cmp;
            }
            else {
                name = field.name;
                cmp = getCmpFunc(field.primer, field.reverse);
            }
            fields.push({
                name: name,
                cmp: cmp
            });
        }

        // final comparison function
        return function(A, B) {
            var a, b, name, result;
            for (var i = 0; i < n_fields; i++) {
                result = 0;
                field = fields[i];
                name = field.name;

                result = field.cmp(A[name], B[name]);
                if (result !== 0) break;
            }
            return result;
        }
    }
}());

Contoh penggunaan:

homes.sort(sort_by('city', {name:'price', primer: parseInt, reverse: true}));

DEMO


Fungsi asli:

var sort_by = function() {
   var fields = [].slice.call(arguments),
       n_fields = fields.length;

   return function(A,B) {
       var a, b, field, key, primer, reverse, result, i;

       for(i = 0; i < n_fields; i++) {
           result = 0;
           field = fields[i];

           key = typeof field === 'string' ? field : field.name;

           a = A[key];
           b = B[key];

           if (typeof field.primer  !== 'undefined'){
               a = field.primer(a);
               b = field.primer(b);
           }

           reverse = (field.reverse) ? -1 : 1;

           if (a<b) result = reverse * -1;
           if (a>b) result = reverse * 1;
           if(result !== 0) break;
       }
       return result;
   }
};

DEMO

Felix Kling
sumber
2
Sebagai catatan, fungsi ini masih dapat ditingkatkan dengan preprocessing daftar argumen dan membuat "sort options array" yang seragam. Ini dibiarkan sebagai latihan untuk pembaca;)
Felix Kling
@ Mike: Ok ... akhirnya;) Anda melihatnya lebih kompleks sekarang, karena opsi sudah diproses sebelumnya, tetapi fungsi perbandingan akhir (lihat komentar) jauh lebih sederhana yang (semoga) mengarah ke kinerja yang lebih baik. Semakin banyak opsi penyortiran yang Anda miliki, semakin banyak keuntungan yang Anda miliki dari metode ini.
Felix Kling
166

untuk solusi non-generik, sederhana untuk masalah Anda:

homes.sort(
   function(a, b) {          
      if (a.city === b.city) {
         // Price is only important when cities are the same
         return b.price - a.price;
      }
      return a.city > b.city ? 1 : -1;
   });
Snowburnt
sumber
6
Saya pikir demo ini adalah apa yang diinginkan OP => jsfiddle.net/zJ6UA/533
Amin Jafari
3
Ini memiliki ide yang benar, tetapi logikanya semua salah. Anda tidak dapat mengurangi string non-numerik dari string lain, dan ifpernyataan itu tidak masuk akal.
JLRishe
6
Anda dapat menggunakan a.localeCompare(b)di baris terakhir untuk membandingkan string ... lihat dokumen
Michael P
2
Bukankah perbandingan kota pertama harus memeriksa kesetaraan, bukan ketidaksetaraan? Dengan kata lain, bukankah seharusnya garis itu if (a.city === b.city)? Artinya, jika kedua kota itu sama maka bandingkan harganya, kalau tidak bandingkan kota-kota itu.
Steven Rands
2
salah satu jawaban terbaik. tnx.
jonathana
56

Anda bisa menggunakan pendekatan penyortiran berantai dengan mengambil delta nilai hingga mencapai nilai yang tidak sama dengan nol.

var data = [{ 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" }];

data.sort(function (a, b) {
    return a.city.localeCompare(b.city) || b.price - a.price;
});

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Atau, menggunakan es6, cukup:

data.sort((a, b) => a.city.localeCompare(b.city) || b.price - a.price);
Nina Scholz
sumber
17
Apakah saya melewatkan sesuatu? Mengapa menggunakan 60 baris kode untuk sesuatu yang dapat dilakukan dalam 1. Sederhana, jelas, ringkas. Seharusnya jawaban yang diterima IMO.
Erez Cohen
salah satu masalah besar sekarang dari SO adalah bahwa jawaban lama - sering digantikan dengan solusi yang lebih baik menggunakan fitur bahasa baru (misalnya ES5-6-7) mempertahankan skor lama mereka, dan kita semua harus gulir ke bawah untuk menemukan yang "nyata" terbaik solusi! SO harus kedaluwarsa suara dari waktu ke waktu untuk mengatasi ini, karena masalahnya semakin buruk seiring berjalannya waktu.
Andy Lorenz
53

Berikut ini adalah pendekatan fungsional sederhana. Tentukan urutan pengurutan menggunakan larik. Prepend minus untuk menentukan urutan menurun.

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

homes.sort(fieldSorter(['city', '-price']));
// homes.sort(fieldSorter(['zip', '-state', 'price'])); // alternative

function fieldSorter(fields) {
    return function (a, b) {
        return fields
            .map(function (o) {
                var dir = 1;
                if (o[0] === '-') {
                   dir = -1;
                   o=o.substring(1);
                }
                if (a[o] > b[o]) return dir;
                if (a[o] < b[o]) return -(dir);
                return 0;
            })
            .reduce(function firstNonZeroValue (p,n) {
                return p ? p : n;
            }, 0);
    };
}

Sunting: di ES6 bahkan lebih pendek!

"use strict";
const fieldSorter = (fields) => (a, b) => fields.map(o => {
    let dir = 1;
    if (o[0] === '-') { dir = -1; o=o.substring(1); }
    return a[o] > b[o] ? dir : a[o] < b[o] ? -(dir) : 0;
}).reduce((p, n) => p ? p : n, 0);

const 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}];
const sortedHomes = homes.sort(fieldSorter(['state', '-price']));

document.write('<pre>' + JSON.stringify(sortedHomes, null, '\t') + '</pre>')

chriskelly
sumber
6
Saya menemukan fungsi ini cukup rapi sehingga saya membuat peningkatan kinerja kecil hingga 90% tergantung pada parser. Saya membuat intisari dan test suite .
php_nub_qq
Berdasarkan data sampel sepertinya angka diurutkan seperti yang diharapkan, namun ketika saya mencoba menerapkan nomor ini di mana menyortir lebih seperti string ... [10,100,11,9]. Apakah saya melewatkan sesuatu?
Mark Carpenter Jr
@MarkCarpenterJr. Tidak yakin apa yang kamu maksud. contoh saya mengurutkan jenis numerik dengan benar. Bisakah Anda membagikan implementasi Anda sebagai pertanyaan dan referensi saya di komentar jadi saya melihatnya? Lalu aku bisa memeriksanya.
chriskelly
@MarkCarpenterJr. Baru saja melihatnya. Saya telah menambahkan penjelasan di komentar.
chriskelly
32

Saya membuat penyortir multi fitur yang cukup umum hari ini. Anda dapat melihat thenBy.js di sini: https://github.com/Teun/thenBy.js

Ini memungkinkan Anda untuk menggunakan Array.sort standar, tetapi dengan gaya firstBy (). ThenBy (). ThenBy (). Ini adalah cara dan kompleksitas yang lebih sedikit daripada solusi yang diposting di atas.

Teun D
sumber
8
Nah, ketika Anda menelepon 3 kali, panggilan kedua tidak dijamin untuk meninggalkan urutan yang pertama tidak tersentuh untuk item di mana panggilan kedua tidak membuat perbedaan.
Teun D
13

Fungsi berikut akan memungkinkan Anda untuk mengurutkan array objek pada satu atau beberapa properti, baik naik (default) atau turun di setiap properti, dan memungkinkan Anda untuk memilih apakah akan melakukan perbandingan case sensitif atau tidak. Secara default, fungsi ini melakukan jenis huruf besar-kecil.

Argumen pertama harus berupa array yang berisi objek. Argumen selanjutnya harus berupa daftar string yang dipisah koma yang mereferensikan properti objek yang berbeda untuk disortir. Argumen terakhir (yang opsional) adalah boolean untuk memilih apakah akan melakukan jenis huruf sensitif atau tidak - gunakan trueuntuk jenis huruf sensitif.

Fungsi akan mengurutkan setiap properti / kunci dalam urutan naik secara default. Jika Anda ingin kunci tertentu untuk semacam dalam urutan, maka sebagai gantinya lulus dalam sebuah array dalam format ini: ['property_name', true].

Berikut adalah beberapa contoh penggunaan fungsi yang diikuti oleh penjelasan (di mana homesarray berisi objek):

objSort(homes, 'city') -> urutkan berdasarkan kota (ascending, case in-sensitive)

objSort(homes, ['city', true]) -> urutkan berdasarkan kota (turun, case-in sensitif)

objSort(homes, 'city', true)-> urutkan berdasarkan kota kemudian harga (naik, peka huruf besar kecil )

objSort(homes, 'city', 'price') -> urutkan berdasarkan kota kemudian harga (keduanya naik, tidak sensitif huruf)

objSort(homes, 'city', ['price', true]) -> urutkan berdasarkan kota (menanjak) lalu harga (turun), tidak sensitif kasus)

Dan tanpa basa-basi lagi, inilah fungsinya:

function objSort() {
    var args = arguments,
        array = args[0],
        case_sensitive, keys_length, key, desc, a, b, i;

    if (typeof arguments[arguments.length - 1] === 'boolean') {
        case_sensitive = arguments[arguments.length - 1];
        keys_length = arguments.length - 1;
    } else {
        case_sensitive = false;
        keys_length = arguments.length;
    }

    return array.sort(function (obj1, obj2) {
        for (i = 1; i < keys_length; i++) {
            key = args[i];
            if (typeof key !== 'string') {
                desc = key[1];
                key = key[0];
                a = obj1[args[i][0]];
                b = obj2[args[i][0]];
            } else {
                desc = false;
                a = obj1[args[i]];
                b = obj2[args[i]];
            }

            if (case_sensitive === false && typeof a === 'string') {
                a = a.toLowerCase();
                b = b.toLowerCase();
            }

            if (! desc) {
                if (a < b) return -1;
                if (a > b) return 1;
            } else {
                if (a > b) return -1;
                if (a < b) return 1;
            }
        }
        return 0;
    });
} //end of objSort() function

Dan inilah beberapa contoh data:

var homes = [{
    "h_id": "3",
    "city": "Dallas",
    "state": "TX",
    "zip": "75201",
    "price": 162500
}, {
    "h_id": "4",
    "city": "Bevery Hills",
    "state": "CA",
    "zip": "90210",
    "price": 1000000
}, {
    "h_id": "5",
    "city": "new york",
    "state": "NY",
    "zip": "00010",
    "price": 1000000
}, {
    "h_id": "6",
    "city": "Dallas",
    "state": "TX",
    "zip": "85000",
    "price": 300000
}, {
    "h_id": "7",
    "city": "New York",
    "state": "NY",
    "zip": "00020",
    "price": 345000
}];
jake
sumber
8

Ini adalah cheat yang lengkap, tetapi saya pikir itu menambah nilai untuk pertanyaan ini karena pada dasarnya fungsi library kalengan yang dapat Anda gunakan di luar kotak.

Jika kode Anda memiliki akses ke lodashatau pustaka Lodash yang kompatibel seperti underscoremaka Anda dapat menggunakan _.sortBymetode ini. Cuplikan di bawah ini disalin langsung dari dokumentasi lodash .

Hasil yang dikomentari dalam contoh-contoh tersebut terlihat seperti mengembalikan array array tetapi itu hanya menunjukkan urutan dan bukan hasil aktual yang merupakan array objek.

var users = [
  { 'user': 'fred',   'age': 48 },
  { 'user': 'barney', 'age': 36 },
  { 'user': 'fred',   'age': 40 },
  { 'user': 'barney', 'age': 34 }
];

_.sortBy(users, [function(o) { return o.user; }]);
 // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]

_.sortBy(users, ['user', 'age']);
// => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]
Orang
sumber
7

Berikut ini satu lagi yang mungkin lebih dekat dengan ide Anda untuk sintaksis

function sortObjects(objArray, properties /*, primers*/) {
    var primers = arguments[2] || {}; // primers are optional

    properties = properties.map(function(prop) {
        if( !(prop instanceof Array) ) {
            prop = [prop, 'asc']
        }
        if( prop[1].toLowerCase() == 'desc' ) {
            prop[1] = -1;
        } else {
            prop[1] = 1;
        }
        return prop;
    });

    function valueCmp(x, y) {
        return x > y ? 1 : x < y ? -1 : 0; 
    }

    function arrayCmp(a, b) {
        var arr1 = [], arr2 = [];
        properties.forEach(function(prop) {
            var aValue = a[prop[0]],
                bValue = b[prop[0]];
            if( typeof primers[prop[0]] != 'undefined' ) {
                aValue = primers[prop[0]](aValue);
                bValue = primers[prop[0]](bValue);
            }
            arr1.push( prop[1] * valueCmp(aValue, bValue) );
            arr2.push( prop[1] * valueCmp(bValue, aValue) );
        });
        return arr1 < arr2 ? -1 : 1;
    }

    objArray.sort(function(a, b) {
        return arrayCmp(a, b);
    });
}

// just for fun use this to reverse the city name when sorting
function demoPrimer(str) {
    return str.split('').reverse().join('');
}

// Example
sortObjects(homes, ['city', ['price', 'desc']], {city: demoPrimer});

Demo: http://jsfiddle.net/Nq4dk/2/


Sunting: Hanya untuk bersenang-senang, inilah variasi yang hanya membutuhkan string mirip sql, jadi Anda bisa melakukannyasortObjects(homes, "city, price desc")

function sortObjects(objArray, properties /*, primers*/) {
    var primers = arguments[2] || {};

    properties = properties.split(/\s*,\s*/).map(function(prop) {
        prop = prop.match(/^([^\s]+)(\s*desc)?/i);
        if( prop[2] && prop[2].toLowerCase() === 'desc' ) {
            return [prop[1] , -1];
        } else {
            return [prop[1] , 1];
        }
    });

    function valueCmp(x, y) {
        return x > y ? 1 : x < y ? -1 : 0; 
    }

    function arrayCmp(a, b) {
        var arr1 = [], arr2 = [];
        properties.forEach(function(prop) {
            var aValue = a[prop[0]],
                bValue = b[prop[0]];
            if( typeof primers[prop[0]] != 'undefined' ) {
                aValue = primers[prop[0]](aValue);
                bValue = primers[prop[0]](bValue);
            }
            arr1.push( prop[1] * valueCmp(aValue, bValue) );
            arr2.push( prop[1] * valueCmp(bValue, aValue) );
        });
        return arr1 < arr2 ? -1 : 1;
    }

    objArray.sort(function(a, b) {
        return arrayCmp(a, b);
    });
}
Flambino
sumber
solusi ini bersih tetapi tidak berkinerja karena perbandingan array. Anda hanya dapat melakukan melihat-lihat properti melacak nilai yang dibandingkan dan itu bukan nol, kembali. itu jauh lebih cepat.
amankapur91
4

Lebih sederhana:

var someArray = [...];

function generateSortFn(props) {
    return function (a, b) {
        for (var i = 0; i < props.length; i++) {
            var prop = props[i];
            var name = prop.name;
            var reverse = prop.reverse;
            if (a[name] < b[name])
                return reverse ? 1 : -1;
            if (a[name] > b[name])
                return reverse ? -1 : 1;
        }
        return 0;
    };
};

someArray.sort(generateSortFn([{name: 'prop1', reverse: true}, {name: 'prop2'}]));
Ravshan Samandarov
sumber
3

Saya suka pendekatan SnowBurnt tetapi perlu tweak untuk menguji kesetaraan pada kota BUKAN perbedaan.

homes.sort(
   function(a,b){
      if (a.city==b.city){
         return (b.price-a.price);
      } else {
         return (a.city-b.city);
      }
   });
james kenny
sumber
3

Berikut adalah jenis multidimensi generik, memungkinkan untuk membalikkan dan / atau memetakan pada setiap level.

Ditulis dalam naskah. Untuk Javascript, lihat JSFiddle ini

Kode

type itemMap = (n: any) => any;

interface SortConfig<T> {
  key: keyof T;
  reverse?: boolean;
  map?: itemMap;
}

export function byObjectValues<T extends object>(keys: ((keyof T) | SortConfig<T>)[]): (a: T, b: T) => 0 | 1 | -1 {
  return function(a: T, b: T) {
    const firstKey: keyof T | SortConfig<T> = keys[0];
    const isSimple = typeof firstKey === 'string';
    const key: keyof T = isSimple ? (firstKey as keyof T) : (firstKey as SortConfig<T>).key;
    const reverse: boolean = isSimple ? false : !!(firstKey as SortConfig<T>).reverse;
    const map: itemMap | null = isSimple ? null : (firstKey as SortConfig<T>).map || null;

    const valA = map ? map(a[key]) : a[key];
    const valB = map ? map(b[key]) : b[key];
    if (valA === valB) {
      if (keys.length === 1) {
        return 0;
      }
      return byObjectValues<T>(keys.slice(1))(a, b);
    }
    if (reverse) {
      return valA > valB ? -1 : 1;
    }
    return valA > valB ? 1 : -1;
  };
}

Contoh Penggunaan

Mengurutkan susunan orang berdasarkan nama belakang, lalu nama depan:

interface Person {
  firstName: string;
  lastName: string;
}

people.sort(byObjectValues<Person>(['lastName','firstName']));

Urutkan kode bahasa dengan nama mereka , bukan kode bahasa mereka (lihat map), lalu dengan versi menurun (lihat reverse).

interface Language {
  code: string;
  version: number;
}

// languageCodeToName(code) is defined elsewhere in code

languageCodes.sort(byObjectValues<Language>([
  {
    key: 'code',
    map(code:string) => languageCodeToName(code),
  },
  {
    key: 'version',
    reverse: true,
  }
]));
Joshua Hansen
sumber
2

Cara dinamis untuk melakukannya dengan kunci GANDA:

  • filter nilai unik dari setiap col / key sort
  • menertibkan atau membalikkannya
  • menambahkan bobot zeropad lebar untuk setiap objek berdasarkan nilai kunci indexOf (nilai)
  • mengurutkan menggunakan bobot caclutated

masukkan deskripsi gambar di sini

Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) { 
    sorts.map(sort => {            
        sort.uniques = Array.from(
            new Set(this.map(obj => obj[sort.key]))
        );

        sort.uniques = sort.uniques.sort((a, b) => {
            if (typeof a == 'string') {
                return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
            }
            else if (typeof a == 'number') {
                return sort.inverse ? (a < b) : (a > b ? 1 : 0);
            }
            else if (typeof a == 'boolean') {
                let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
                return x;
            }
            return 0;
        });
    });

    const weightOfObject = (obj) => {
        let weight = "";
        sorts.map(sort => {
            let zeropad = `${sort.uniques.length}`.length;
            weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
        });
        //obj.weight = weight; // if you need to see weights
        return weight;
    }

    this.sort((a, b) => {
        return weightOfObject(a).localeCompare( weightOfObject(b) );
    });

    return this;
}
});

Menggunakan:

// works with string, number and boolean
let sortered = your_array.orderBy([
    {key: "type", inverse: false}, 
    {key: "title", inverse: false},
    {key: "spot", inverse: false},
    {key: "internal", inverse: true}
]);

masukkan deskripsi gambar di sini

Leonardo Filipe
sumber
1

Ini adalah versi generik dari solusi @ Snowburnt:

var sortarray = [{field:'city', direction:'asc'}, {field:'price', direction:'desc'}];
array.sort(function(a,b){
    for(var i=0; i<sortarray.length; i++){
        retval = a[sortarray[i].field] < b[sortarray[i].field] ? -1 : a[sortarray[i].field] > b[sortarray[i].field] ? 1 : 0;
        if (sortarray[i].direction == "desc") {
            retval = retval * -1;
        }
        if (retval !== 0) {
            return retval;
        }
    }
}


})

Ini didasarkan pada semacam rutin yang saya gunakan. Saya tidak menguji kode khusus ini sehingga mungkin ada kesalahan tetapi Anda mendapatkan idenya. Idenya adalah untuk mengurutkan berdasarkan bidang pertama yang menunjukkan perbedaan dan kemudian berhenti dan pergi ke catatan berikutnya. Jadi, jika Anda mengurutkan berdasarkan tiga bidang dan bidang pertama dalam perbandingan sudah cukup untuk menentukan urutan pengurutan dari dua rekaman yang diurutkan maka kembalikan hasil pengurutan itu dan pergi ke catatan berikutnya.

Saya mengujinya (sebenarnya dengan sedikit lebih rumit semacam logika) pada 5000 catatan dan itu melakukannya dalam sekejap mata. Jika Anda benar-benar memuat lebih dari 1000 catatan ke klien, Anda mungkin harus menggunakan pemilahan dan pemilahan sisi-sisi.

Kode ini tidak menangani sensitivitas huruf besar tetapi saya menyerahkannya kepada pembaca untuk menangani modifikasi sepele ini.

HisDivineShadow
sumber
1

Inilah solusi saya berdasarkan idiom transformasi Schwartzian , semoga bermanfaat.

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: 'Pako',              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
1
Saya mencoba beberapa hal di ini (dan halaman lain). Solusi dengan a8m ini hanya satu untuk situasi saya: gist.github.com/cemerson/f1f1434286c1262b403f3d85c96688e0
Christopher D. Emerson
1

Cara lain

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"}
    ];
function sortBy(ar) {
  return ar.sort((a, b) => a.city === b.city ?
      b.price.toString().localeCompare(a.price) :
      a.city.toString().localeCompare(b.city));
}
console.log(sortBy(homes));

Mihai
sumber
0
function sortMultiFields(prop){
    return function(a,b){
        for(i=0;i<prop.length;i++)
        {
            var reg = /^\d+$/;
            var x=1;
            var field1=prop[i];
            if(prop[i].indexOf("-")==0)
            {
                field1=prop[i].substr(1,prop[i].length);
                x=-x;
            }

            if(reg.test(a[field1]))
            {
                a[field1]=parseFloat(a[field1]);
                b[field1]=parseFloat(b[field1]);
            }
            if( a[field1] > b[field1])
                return x;
            else if(a[field1] < b[field1])
                return -x;
        }
    }
}

Cara menggunakan (put - (minus) tandatangani sebelum bidang jika Anda ingin mengurutkan dalam bidang tertentu urutan menurun)

homes.sort(sortMultiFields(["city","-price"]));

Menggunakan fungsi di atas, Anda dapat mengurutkan json array dengan beberapa bidang. Tidak perlu mengubah fungsi tubuh sama sekali

Nikhil sHETH
sumber
0

Adaptasi dari jawaban @chriskelly.


Sebagian besar jawaban mengabaikan bahwa harga tidak akan diurutkan dengan benar jika nilainya dalam sepuluh ribu dan lebih rendah atau lebih dari satu juta. Respondasinya adalah jenis JS abjad. Itu dijawab dengan cukup baik di sini, Mengapa JavaScript tidak bisa mengurutkan "5, 10, 1" dan di sini Cara mengurutkan array bilangan bulat dengan benar .

Pada akhirnya kita harus melakukan beberapa evaluasi jika bidang atau simpul yang kita sortir adalah angka. Saya tidak mengatakan bahwa menggunakan parseInt()dalam hal ini adalah jawaban yang benar, hasil yang diurutkan lebih penting.

var homes = [{
  "h_id": "2",
  "city": "Dallas",
  "state": "TX",
  "zip": "75201",
  "price": "62500"
}, {
  "h_id": "1",
  "city": "Dallas",
  "state": "TX",
  "zip": "75201",
  "price": "62510"
}, {
  "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"
}];

homes.sort(fieldSorter(['price']));
// homes.sort(fieldSorter(['zip', '-state', 'price'])); // alternative

function fieldSorter(fields) {
  return function(a, b) {
    return fields
      .map(function(o) {
        var dir = 1;
        if (o[0] === '-') {
          dir = -1;
          o = o.substring(1);
        }
        if (!parseInt(a[o]) && !parseInt(b[o])) {
          if (a[o] > b[o]) return dir;
          if (a[o] < b[o]) return -(dir);
          return 0;
        } else {
          return dir > 0 ? a[o] - b[o] : b[o] - a[o];
        }
      })
      .reduce(function firstNonZeroValue(p, n) {
        return p ? p : n;
      }, 0);
  };
}
document.getElementById("output").innerHTML = '<pre>' + JSON.stringify(homes, null, '\t') + '</pre>';
<div id="output">

</div>


Biola untuk diuji

Mark Carpenter Jr
sumber
Masalahnya adalah dengan data yang Anda coba untuk mengurutkan. pricedalam contoh ini dalam format string. Jika Anda ingin itu berfungsi dengan baik dengan contoh saya gunakan peta untuk mengkonversi bidang Anda ingin format angka terlebih dahulu. yaituconst correctedHomes = homes.map(h => ({...h, price: +h.price}))
chriskelly
0

Wow, ada beberapa solusi kompleks di sini. Begitu rumit sehingga saya memutuskan untuk membuat sesuatu yang lebih sederhana tetapi juga cukup kuat. Ini dia;

function sortByPriority(data, priorities) {
  if (priorities.length == 0) {
    return data;
  }

  const nextPriority = priorities[0];
  const remainingPriorities = priorities.slice(1);

  const matched = data.filter(item => item.hasOwnProperty(nextPriority));
  const remainingData = data.filter(item => !item.hasOwnProperty(nextPriority));

  return sortByPriority(matched, remainingPriorities)
    .sort((a, b) => (a[nextPriority] > b[nextPriority]) ? 1 : -1)
    .concat(sortByPriority(remainingData, remainingPriorities));
}

Dan ini adalah contoh bagaimana Anda menggunakannya.

const data = [
  { id: 1,                         mediumPriority: 'bbb', lowestPriority: 'ggg' },
  { id: 2, highestPriority: 'bbb', mediumPriority: 'ccc', lowestPriority: 'ggg' },
  { id: 3,                         mediumPriority: 'aaa', lowestPriority: 'ggg' },
];

const priorities = [
  'highestPriority',
  'mediumPriority',
  'lowestPriority'
];


const sorted = sortByPriority(data, priorities);

Ini pertama-tama akan mengurutkan berdasarkan prioritas atribut, kemudian oleh nilai atribut.

Steztric
sumber
0

Berikut adalah cara yang dapat diperluas untuk mengurutkan berdasarkan beberapa bidang.

homes.sort(function(left, right) {
    var city_order = left.city.localeCompare(right.city);
    var price_order = parseInt(left.price) - parseInt(right.price);
    return city_order || -price_order;
});

Catatan

  • a.localeCompare(b)adalah universal didukung dan kembali -1,0,1 jika a<b, a==b, a>bmasing-masing.
  • Pengurangan bekerja pada bidang numerik.
  • ||di baris terakhir memberi cityprioritas lebih price.
  • Negasikan untuk membalikkan urutan dalam bidang apa pun, seperti pada -price_order
  • Perbandingan tanggal , var date_order = new Date(left.date) - new Date(right.date);berfungsi seperti angka karena matematika tanggal berubah menjadi milidetik sejak tahun 1970.
  • Tambahkan bidang ke dalam atau -rantai, return city_order || -price_order || date_order;
Bob Stein
sumber
0

Saya pikir ini mungkin cara termudah untuk melakukannya.

https://coderwall.com/p/ebqhca/javascript-sort-by-two-fields

Ini sangat sederhana dan saya mencobanya dengan 3 pasangan nilai kunci yang berbeda dan bekerja dengan sangat baik.

Ini adalah contoh sederhana, lihat tautan untuk detail lebih lanjut

testSort(data) {
    return data.sort(
        a['nameOne'] > b['nameOne'] ? 1
        : b['nameOne'] > a['nameOne'] ? -1 : 0 ||
        a['date'] > b['date'] ||
        a['number'] - b['number']
    );
}
JDinar
sumber
0

Ini milik saya untuk referensi Anda, dengan contoh:

function msort(arr, ...compFns) {
  let fn = compFns[0];
  arr = [].concat(arr);
  let arr1 = [];
  while (arr.length > 0) {
    let arr2 = arr.splice(0, 1);
    for (let i = arr.length; i > 0;) {
      if (fn(arr2[0], arr[--i]) === 0) {
        arr2 = arr2.concat(arr.splice(i, 1));
      }
    }
    arr1.push(arr2);
  }

  arr1.sort(function (a, b) {
    return fn(a[0], b[0]);
  });

  compFns = compFns.slice(1);
  let res = [];
  arr1.map(a1 => {
    if (compFns.length > 0) a1 = msort(a1, ...compFns);
    a1.map(a2 => res.push(a2));
  });
  return res;
}

let tstArr = [{ id: 1, sex: 'o' }, { id: 2, sex: 'm' }, { id: 3, sex: 'm' }, { id: 4, sex: 'f' }, { id: 5, sex: 'm' }, { id: 6, sex: 'o' }, { id: 7, sex: 'f' }];

function tstFn1(a, b) {
  if (a.sex > b.sex) return 1;
  else if (a.sex < b.sex) return -1;
  return 0;
}

function tstFn2(a, b) {
  if (a.id > b.id) return -1;
  else if (a.id < b.id) return 1;
  return 0;
}

console.log(JSON.stringify(msort(tstArr, tstFn1, tstFn2)));
//output:
//[{"id":7,"sex":"f"},{"id":4,"sex":"f"},{"id":5,"sex":"m"},{"id":3,"sex":"m"},{"id":2,"sex":"m"},{"id":6,"sex":"o"},{"id":1,"sex":"o"}]
ritsleting
sumber
0

Saya mencari sesuatu yang serupa dan berakhir dengan ini:

Pertama, kami memiliki satu atau beberapa fungsi penyortiran, selalu mengembalikan 0, 1 atau -1:

const sortByTitle = (a, b): number => 
  a.title === b.title ? 0 : a.title > b.title ? 1 : -1;

Anda dapat membuat lebih banyak fungsi untuk setiap properti lain yang ingin Anda urutkan.

Lalu saya memiliki fungsi yang menggabungkan fungsi penyortiran ini menjadi satu:

const createSorter = (...sorters) => (a, b) =>
  sorters.reduce(
    (d, fn) => (d === 0 ? fn(a, b) : d),
    0
  );

Ini dapat digunakan untuk menggabungkan fungsi penyortiran di atas dengan cara yang mudah dibaca:

const sorter = createSorter(sortByTitle, sortByYear)

items.sort(sorter)

Ketika fungsi sortir mengembalikan 0, fungsi sortasi selanjutnya akan dipanggil untuk sortasi lebih lanjut.

Soesah
sumber
0

Hanya pilihan lain. Pertimbangkan untuk menggunakan fungsi utilitas berikut:

/** Performs comparing of two items by specified properties
 * @param  {Array} props for sorting ['name'], ['value', 'city'], ['-date']
 * to set descending order on object property just add '-' at the begining of property
 */
export const compareBy = (...props) => (a, b) => {
  for (let i = 0; i < props.length; i++) {
    const ascValue = props[i].startsWith('-') ? -1 : 1;
    const prop = props[i].startsWith('-') ? props[i].substr(1) : props[i];
    if (a[prop] !== b[prop]) {
      return a[prop] > b[prop] ? ascValue : -ascValue;
    }
  }
  return 0;
};

Contoh penggunaan (dalam kasus Anda):

homes.sort(compareBy('city', '-price'));

Perlu dicatat bahwa fungsi ini dapat lebih digeneralisasi untuk dapat menggunakan properti bersarang seperti 'address.city' atau 'style.size.width' dll.

Dmitry Anch
sumber
0

Ini adalah algoritma rekursif untuk mengurutkan berdasarkan beberapa bidang sambil memiliki kesempatan untuk memformat nilai sebelum perbandingan.

var data = [
{
    "id": 1,
    "ship": null,
    "product": "Orange",
    "quantity": 7,
    "price": 92.08,
    "discount": 0
},
{
    "id": 2,
    "ship": "2017-06-14T23:00:00.000Z".toDate(),
    "product": "Apple",
    "quantity": 22,
    "price": 184.16,
    "discount": 0
},
...
]
var sorts = ["product", "quantity", "ship"]

// comp_val formats values and protects against comparing nulls/undefines
// type() just returns the variable constructor
// String.lower just converts the string to lowercase.
// String.toDate custom fn to convert strings to Date
function comp_val(value){
    if (value==null || value==undefined) return null
    var cls = type(value)
    switch (cls){
        case String:
            return value.lower()
    }
    return value
}

function compare(a, b, i){
    i = i || 0
    var prop = sorts[i]
    var va = comp_val(a[prop])
    var vb = comp_val(b[prop])

    // handle what to do when both or any values are null
    if (va == null || vb == null) return true

    if ((i < sorts.length-1) && (va == vb)) {
        return compare(a, b, i+1)
    } 
    return va > vb
}

var d = data.sort(compare);
console.log(d);

Jika a dan b sama, itu hanya akan mencoba bidang berikutnya sampai tidak ada yang tersedia.

Mackraken
sumber
-1
homes.sort(function(a,b) { return a.city - b.city } );
homes.sort(function(a,b){
    if (a.city==b.city){
        return parseFloat(b.price) - parseFloat(a.price);
    } else {
        return 0;
    }
});
Jonan Pineda
sumber
Mengapa tidak memasukkan semuanya ke dalam satu fungsi? Jika kota tidak sama, kembalikan perbedaannya, jika tidak, beda harganya.
Fisikawan Gila
-1

Di sini 'AffiliateDueDate' dan 'Judul' adalah kolom, keduanya diurutkan dalam urutan menaik.

array.sort(function(a, b) {

               if (a.AffiliateDueDate > b.AffiliateDueDate ) return 1;
               else if (a.AffiliateDueDate < b.AffiliateDueDate ) return -1;
               else if (a.Title > b.Title ) return 1;
               else if (a.Title < b.Title ) return -1;
               else return 0;
             })
Amay Kulkarni
sumber
-1

Menyortir pada dua bidang tanggal dan contoh bidang numerik:

var generic_date =  new Date(2070, 1, 1);
checkDate = function(date) {
  return Date.parse(date) ? new Date(date): generic_date;
}

function sortData() {  
  data.sort(function(a,b){
    var deltaEnd = checkDate(b.end) - checkDate(a.end);
    if(deltaEnd) return deltaEnd;

    var deltaRank = a.rank - b.rank;
    if (deltaRank) return deltaRank;

    var deltaStart = checkDate(b.start) - checkDate(a.start);
    if(deltaStart) return deltaStart;

    return 0;
  });
}

http://jsfiddle.net/hcWgf/57/

Igor Vaschuk
sumber
-1
function sort(data, orderBy) {
        orderBy = Array.isArray(orderBy) ? orderBy : [orderBy];
        return data.sort((a, b) => {
            for (let i = 0, size = orderBy.length; i < size; i++) {
                const key = Object.keys(orderBy[i])[0],
                    o = orderBy[i][key],
                    valueA = a[key],
                    valueB = b[key];
                if (!(valueA || valueB)) {
                    console.error("the objects from the data passed does not have the key '" + key + "' passed on sort!");
                    return [];
                }
                if (+valueA === +valueA) {
                    return o.toLowerCase() === 'desc' ? valueB - valueA : valueA - valueB;
                } else {
                    if (valueA.localeCompare(valueB) > 0) {
                        return o.toLowerCase() === 'desc' ? -1 : 1;
                    } else if (valueA.localeCompare(valueB) < 0) {
                        return o.toLowerCase() === 'desc' ? 1 : -1;
                    }
                }
            }
        });
    }

Menggunakan:

sort(homes, [{city : 'asc'}, {price: 'desc'}])

Elias Pinheiro
sumber
-1

Bagaimana dengan solusi sederhana ini:

const sortCompareByCityPrice = (a, b) => {
    let comparison = 0
    // sort by first criteria
    if (a.city > b.city) {
        comparison = 1
    }
    else if (a.city < b.city) {
        comparison = -1
    }
    // If still 0 then sort by second criteria descending
    if (comparison === 0) {
        if (parseInt(a.price) > parseInt(b.price)) {
            comparison = -1
        }
        else if (parseInt(a.price) < parseInt(b.price)) {
            comparison = 1
        }
    }
    return comparison 
}

Berdasarkan pertanyaan ini, susunan javascript sortir dengan beberapa bidang (angka)

ramvanet
sumber