Bir nesne dizisini birden çok alana göre nasıl sıralayabilirim?


148

Bu orijinal sorudan , bir sıralama birden çok alana nasıl uygulanır?

Bu biraz uyarlanmış yapıyı kullanarak şehri (artan) ve sonra fiyatı (azalan) nasıl sıralayabilirim?

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

Genel bir yaklaşım sağlayan bir cevap verilmesinden çok hoşuma gitti . Bu kodu kullanmayı planladığım yerde, tarihleri ​​ve diğer şeyleri sıralamak zorunda kalacağım. Biraz hantal olmasa bile, nesneyi "hazırlama" yeteneği kullanışlı görünüyordu.

Bu cevabı güzel bir genel örnek haline getirmeye çalıştım , ama fazla şansım yok.


Aramak veya sıralamak ister misiniz?
Felix Kling

Bağladığınız ikinci yanıtı kullanırken yaşadığınız sorun tam olarak nedir?
canon

Yeterince genel değil. Ben sadece söylemek istiyorum zaman bir kod deniz eklemek gibi görünüyor sort(["first-field", "ASC"], ["second-field", "DSC"]); Ben tarihleri, büyük-küçük harf duyarsızlık vb ele böylece ilk cevap "primer" mantığı eklemek çalıştığınızda bu daha karmaşık.
Mike

Veya her alana ağırlık uygulayabilirsiniz
onmyway133

Lodash kullanarak sorun yaşıyorsanız , lodash.com/docs/4.17.11#orderBy'yi kontrol edebilirsiniz
Deepanshu Arora

Yanıtlar:


83

Bu yanıtı temel alan çok boyutlu bir sıralama yöntemi :

Güncelleme : İşte "optimize edilmiş" bir versiyon. Çok daha fazla önişleme yapar ve önceden her bir sıralama seçeneği için bir karşılaştırma işlevi oluşturur. Daha fazla belleğe ihtiyaç duyabilir (her bir sıralama seçeneği için bir işlev saklar, ancak karşılaştırma sırasında doğru ayarları belirlemek zorunda olmadığından biraz daha iyi bir şekilde biçimlendirilmelidir.

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

Örnek kullanım:

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

DEMO


Orijinal fonksiyon:

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


2
Kayıt için, bu işlev argüman listesini önceden işleyerek ve tek tip bir "sıralama seçenekleri dizisi" oluşturarak geliştirilebilir. Bu okuyucu için egzersiz olarak bırakılmıştır;)
Felix Kling

@Mike: Tamam ... nihayet;) Seçenekler önceden işlendiği için şimdi daha karmaşık olduğunu görüyorsunuz, ancak son karşılaştırma işlevi (açıklamaya bakın) çok daha basittir (umarım) daha iyi performans sağlar. Ne kadar fazla sıralama seçeneğiniz varsa, bu yöntemden o kadar fazla avantajınız olur.
Felix Kling

168

sorununuzun genel olmayan, basit bir çözümü için:

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

6
Bence bu demo OP'nin istediği şeydir
Amin Jafari

3
Bunun doğru fikri var, ama mantık tamamen yanlış. Sayısal olmayan bir dizeyi başka bir dizeden çıkaramazsınız ve ififade mantıklı değildir.
JLRishe

6
a.localeCompare(b)Dize karşılaştırması için son satırda kullanabilirsiniz ... bkz. Dokümanlar
Michael P

2
İlk şehir karşılaştırması eşitliği değil eşitliği kontrol etmeli mi? Başka bir deyişle, çizgi olmamalı if (a.city === b.city)mı? Yani, iki şehir aynı ise fiyatları karşılaştırın, aksi takdirde şehirleri karşılaştırın.
Steven Rands

2
en iyi cevaplardan biri. tnx.
jonathana

56

Sıfıra eşit olmayan bir değere ulaşıncaya kadar değerlerin deltasını alarak zincirleme bir sıralama yaklaşımı kullanabilirsiniz.

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

Veya es6 kullanarak basitçe:

data.sort((a, b) => a.city.localeCompare(b.city) || b.price - a.price);

17
Bir şey mi kaçırıyorum? Neden 1'de yapılabilecek bir şey için 60 satır kod kullanalım? Basit, açık, özlü. Kabul edilen cevap IMO olmalıdır.
Erez Cohen

SO'nun şu andaki büyük sorunlarından biri eski cevaplar - genellikle yeni dil özelliklerini kullanan daha iyi çözümlerin (örn. ES5-6-7) eski puanlarını koruduğu ve hepimizin "en iyi" olanı bulmak için aşağı kaydırmamız gerektiğidir. çözümler! SO, zaman içinde oyları sona erdirmeli, çünkü zaman geçtikçe sorun daha da kötüleşiyor.
Andy Lorenz

53

İşte basit bir fonksiyonel yaklaşım. Diziyi kullanarak sıralama düzenini belirtin. Azalan düzeni belirtmek için eksi işaretini ekleyin .

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

Düzenleme: ES6'da daha da kısa!

"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>')


6
Bu işlevi oldukça düzgün buldum, bu yüzden ayrıştırıcıya bağlı olarak% 90'a kadar küçük bir performans artışı sağladım. Ben bir öz ve test paketi yaptım .
php_nub_qq

Beklendiği gibi sayılar benziyor örnek verilere dayanarak ... Daha dizeleri gibi sıralama nerede bu numaralar uygulayan çalıştı ancak ne zaman sıralanır [10,100,11,9]. Bir şey mi kaçırdım?
Mark Carpenter Jr

@MarkCarpenterJr. Ne demek istediğinden emin değilim. örneğim sayısal türleri doğru şekilde sıralıyor. Uygulamanızı bir soru olarak paylaşabilir ve yorumlarda bana referans verebilir misiniz? Sonra kontrol edebilirim.
chriskelly

@MarkCarpenterJr. Sadece gördüm. Yorumlara bir açıklama ekledim.
chriskelly

32

Bugün oldukça genel bir çok özellikli ayırıcı yaptım. ThenBy.js'ye buradan göz atabilirsiniz: https://github.com/Teun/thenBy.js

Standart Array.sort'u kullanmanızı sağlar, ancak firstBy (). ThenBy (). ThenBy () stiliyle. Yukarıda belirtilen çözümlerden çok daha az kod ve karmaşıklıktır.


8
3 kez aradığınızda, ikinci aramanın bir fark yaratmadığı öğeler için ikinci aramanın sırasına dokunulmaması garanti edilmez.
Teun D

13

Aşağıdaki işlev, bir nesneyi bir veya birden çok özellik üzerinde, her bir özellik üzerinde artan (varsayılan) veya azalan olarak sıralamanıza ve büyük / küçük harfe duyarlı karşılaştırmalar yapıp yapmayacağınızı seçmenize olanak tanır. Varsayılan olarak, bu işlev büyük / küçük harfe duyarlı olmayan türler gerçekleştirir.

İlk argüman nesneleri içeren dizi olmalıdır. Sonraki bağımsız değişken (ler), sıralamak için farklı nesne özelliklerine başvuran virgülle ayrılmış dizeler listesi olmalıdır. Son argüman (isteğe bağlı), büyük / küçük harfe duyarlı türlerin gerçekleştirilip gerçekleştirilmeyeceğini seçmek için bir boole değeridir - büyük / trueküçük harfe duyarlı türler için kullanın .

İşlev, varsayılan olarak her özelliği / anahtarı artan düzende sıralar. Eğer azalan düzende sıralamak için belirli bir anahtar istiyorsanız, o zaman onun yerine bu formatta bir dizi geçmek: ['property_name', true].

Burada işlevin bazı örnek kullanımları ve ardından bir açıklama ( homesnesneleri içeren bir dizi):

objSort(homes, 'city') -> şehre göre sırala (artan, büyük / küçük harfe duyarlı)

objSort(homes, ['city', true]) -> şehre göre sırala (azalan, büyük / küçük harfe duyarlı)

objSort(homes, 'city', true)-> şehre göre sırala fiyat (artan, büyük / küçük harfe duyarlı )

objSort(homes, 'city', 'price') -> şehre ve sonra fiyata göre sırala (her ikisi de artan, büyük / küçük harfe duyarlı)

objSort(homes, 'city', ['price', true]) -> şehre göre sırala (artan) sonra fiyat (azalan), büyük / küçük harfe duyarlı)

Ve daha fazla uzatmadan, işte fonksiyon:

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

Ve işte bazı örnek veriler:

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

8

Bu tam bir hile ama bence bu soruya değer katıyor çünkü temelde kutunun dışında kullanabileceğiniz bir konserve kütüphane işlevi.

Kodunuzun erişimi lodashveya benzeri bir uyumlu kitaplığa sahipse underscore, _.sortByyöntemi kullanabilirsiniz . Aşağıdaki pasaj doğrudan sadakat belgelerinden kopyalanır .

Örneklerdeki yorumlanan sonuçlar, dizilerin dizilerini döndürdüğü gibi görünüyor, ancak bu, bir nesne dizisi olan gerçek sonuçları değil, yalnızca düzeni gösteriyor.

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]]

7

İşte sözdizimi fikrinize belki de daha yakın olan bir tane daha

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/


Düzenleme: Sadece eğlence için, sadece sql benzeri bir dize alan bir varyasyon , böylece yapabilirsinizsortObjects(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);
    });
}

bu çözüm temizdir ancak dizi karşılaştırması nedeniyle performans göstermez. basitçe, karşılaştırılan değeri takip eden özellikleri inceleyebilirsiniz ve sıfır değil, geri dönün. bu çok daha hızlı.
amankapur91

4

Daha basit olan:

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

4

İşte her seviyede tersine ve / veya haritalamaya izin veren genel çok boyutlu bir sıralama.

Daktilo Yazısı. Javascript için bu JSFiddle'a göz atın

Kod

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

Kullanım örnekleri

Bir kişi dizisini soyadına, ardından ada göre sıralama:

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

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

Dil kodlarını isimlerine göre sıralayın, dil kodlarına göre değil (bkz. map), Ardından azalan sürüme göre (bkz. 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,
  }
]));

3

SnowBurnt yaklaşımını seviyorum ama bir fark DEĞİL şehir üzerinde denklik test etmek için bir tweak gerekiyor.

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

2

MULTIPLE tuşlarıyla bunu yapmanın dinamik bir yolu:

  • her bir col / key anahtarından benzersiz değerleri filtreleyin
  • sıraya koy veya tersine çevir
  • indexOf (değer) anahtarları değerlerine dayalı olarak her nesne için ağırlık genişliği zeropad ekleyin
  • caclutated ağırlıkları kullanarak sıralama

resim açıklamasını buraya girin

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

kullanın:

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

resim açıklamasını buraya girin


1

İşte @ Snowburnt çözümünün genel bir versiyonu:

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


})

Bu kullandığım bir sıralama rutinine dayanmaktadır. Bu belirli kodu test etmedim, bu yüzden hatalar olabilir, ancak fikri anlıyorsunuz. Fikir, farkı gösteren ilk alana göre sıralamak ve ardından durup bir sonraki kayda gitmek. Dolayısıyla, üç alana göre sıralama yapıyorsanız ve karşılaştırmanın ilk alanı, sıralanan iki kaydın sıralama düzenini belirlemek için yeterliyse, bu sıralama sonucunu döndürün ve bir sonraki kayda geçin.

5000 kayıt üzerinde test ettim (aslında biraz daha karmaşık sıralama mantığıyla) ve göz açıp kapayıncaya kadar yaptım. İstemciye aslında 1000'den fazla kayıt yüklüyorsanız, büyük olasılıkla sunucu tarafı sıralama ve filtreleme kullanıyor olmanız gerekir.

Bu kod büyük / küçük harfe duyarlılığı işlemiyor, ancak bu önemsiz değişikliği işlemek için okuyucuya bırakıyorum.


1

İşte benim Schwartzian dönüşüm deyimine dayanan çözümüm , umarım faydalı bulursunuz.

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

İşte nasıl kullanılacağına dair bir örnek:

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

1
Bu konuda (ve diğer sayfalarda) birkaç şey denedim. A8m tarafından bu çözüm benim durumum için çalışmak için sadece biriydi: gist.github.com/cemerson/f1f1434286c1262b403f3d85c96688e0
Christopher D. Emerson

1

Diğer yol

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


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

Azalan düzende belirli bir alanda sıralamak istiyorsanız, alanın önüne (koy - (eksi) işareti) nasıl kullanılır?

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

Yukarıdaki işlevi kullanarak herhangi bir json dizisini birden fazla alana göre sıralayabilirsiniz. Fonksiyon gövdesini değiştirmeye gerek yok


0

@Chriskelly'nin cevabının uyarlanması.


Çoğu cevap, değer on bin ve daha düşük veya bir milyonun üzerindeyse fiyatın düzgün sıralanmayacağını göz ardı eder. JS olmak resaon alfabetik olarak sıralar. Burada oldukça iyi yanıtlandı, JavaScript neden "5, 10, 1" ve burada sıralamıyor Bir tamsayı dizisini doğru sıralamak için .

Sonuçta, sıraladığımız alan veya düğüm bir sayı ise, biraz değerlendirme yapmak zorundayız. parseInt()Bu durumda kullanmanın doğru cevap olduğunu söylemiyorum , sıralanan sonuçların daha önemli olduğunu söylemiyorum .

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>


Test etmek için bir keman


Sorun, sıralamaya çalıştığınız verilerde. priceörnekte dize biçimindedir. Örneğim ile düzgün çalışmasını istiyorsanız, önce sayı biçimine dönüştürmek istediğiniz alanı dönüştürmek için map komutunu kullanın. ieconst correctedHomes = homes.map(h => ({...h, price: +h.price}))
chriskelly

0

Vay, burada bazı karmaşık çözümler var. Çok karmaşık, daha basit ama aynı zamanda oldukça güçlü bir şey bulmaya karar verdim. İşte burada;

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

İşte onu nasıl kullandığınıza bir örnek.

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

Bu önce özniteliklerin önceliğine, daha sonra özniteliklerin değerine göre sıralar.


0

Birden çok alana göre sıralamanın genişletilebilir bir yolu.

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

notlar

  • a.localeCompare(b)olduğu evrensel olarak desteklenen ve döner -1,0,1 eğer a<b, a==b, a>bsırasıyla.
  • Çıkarma sayısal alanlarda çalışır.
  • ||son satırda cityöncelik verilir price.
  • Her alanda olduğu gibi ters sırada sıralamak -price_order
  • Tarih karşılaştırması , var date_order = new Date(left.date) - new Date(right.date);sayısal gibi çalışır çünkü tarih matematiği 1970'den beri milisaniyeye dönüşür.
  • Veya zincire alan ekleyin, return city_order || -price_order || date_order;

0

Bence bu bunu yapmanın en kolay yolu olabilir.

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

Gerçekten basit ve 3 farklı anahtar / değer çiftleri ile denedim ve harika çalıştı.

İşte basit bir örnek, daha fazla ayrıntı için bağlantıya bakın

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

0

İşte referans için benim, örneğin:

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

0

Ben benzer bir şey arıyordu ve bununla sona erdi:

İlk olarak, her zaman 0, 1 veya -1 döndüren bir veya daha fazla sıralama fonksiyonuna sahibiz:

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

Sıralamak istediğiniz her bir özellik için daha fazla işlev oluşturabilirsiniz.

Sonra bu sıralama işlevlerini bir araya getiren bir işlevi var:

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

Bu, yukarıdaki sıralama işlevlerini okunabilir bir şekilde birleştirmek için kullanılabilir:

const sorter = createSorter(sortByTitle, sortByYear)

items.sort(sorter)

Bir sıralama işlevi 0 değerini döndürdüğünde, sonraki sıralama için bir sonraki sıralama işlevi çağrılır.


0

Sadece başka bir seçenek. Aşağıdaki yardımcı program işlevini kullanmayı düşünün:

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

Kullanım örneği (sizin durumunuzda):

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

'Address.city' veya 'style.size.width' vb. Gibi iç içe özellikleri kullanabilmek için bu işlevin daha da genelleştirilebileceğine dikkat edilmelidir.


0

Bu, karşılaştırmadan önce değerleri biçimlendirme şansına sahipken, birden çok alana göre sıralamak için yinelenen bir algoritmadır.

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

A ve b eşitse, hiçbiri kullanılabilir olana kadar bir sonraki alanı dener.


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

Neden her şeyi tek bir işleve sokmuyorsunuz? Eğer şehir eşit değilse, onların farkını iade et, yoksa fiyatı fark et.
Mad Physicist

-1

Burada 'AffiliateDueDate' ve 'Title' sütunlarıdır, her ikisi de artan sırada sıralanmıştır.

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

-1

İki tarih alanında sıralama ve sayısal alan örneği:

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/


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

Kullanma:

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


-1

Bu basit çözüme ne dersiniz:

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 
}

Bu soruya göre javascript dizisini birden çok (sayı) alana göre sırala

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.