Làm thế nào để sắp xếp một mảng các đối tượng theo nhiều lĩnh vực?


147

Từ câu hỏi ban đầu này , tôi sẽ áp dụng sắp xếp như thế nào trên nhiều lĩnh vực?

Sử dụng cấu trúc hơi thích nghi này, làm thế nào tôi sắp xếp thành phố (tăng dần) và sau đó giá (giảm dần)?

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

Tôi thích thực tế hơn là một câu trả lời được đưa ra trong đó cung cấp một cách tiếp cận chung. Nơi tôi dự định sử dụng mã này, tôi sẽ phải sắp xếp ngày cũng như những thứ khác. Khả năng "tố" đối tượng có vẻ tiện dụng, nếu không muốn nói là hơi cồng kềnh.

Tôi đã cố gắng xây dựng câu trả lời này thành một ví dụ chung chung, nhưng tôi không gặp nhiều may mắn.


Bạn có muốn tìm kiếm hoặc sắp xếp?
Felix Kling

Chính xác thì vấn đề bạn gặp phải khi sử dụng câu trả lời thứ hai mà bạn đã liên kết là gì?
canon

Nó không đủ chung chung. Tôi dường như đang thêm một biển mã khi tôi chỉ đơn giản muốn nói sort(["first-field", "ASC"], ["second-field", "DSC"]); Điều này còn phức tạp hơn khi tôi cố gắng thêm vào logic "mồi" của câu trả lời đầu tiên để tôi có thể xử lý ngày, không phân biệt chữ hoa chữ thường, v.v.
Mike

Hoặc bạn có thể áp dụng trọng số cho từng lĩnh vực
onmyway133

Bạn có thể kiểm tra lodash.com/docs/4.17.11#orderBy , nếu bạn ổn khi sử dụng lodash
Deepanshu Arora

Câu trả lời:


83

Một phương pháp sắp xếp đa chiều, dựa trên câu trả lời này :

Cập nhật : Đây là phiên bản "tối ưu hóa". Nó thực hiện nhiều tiền xử lý hơn và tạo ra một chức năng so sánh cho từng tùy chọn sắp xếp trước đó. Nó có thể cần nhiều bộ nhớ hơn (vì nó lưu trữ một chức năng cho mỗi tùy chọn sắp xếp, nhưng nó sẽ hoạt động tốt hơn một chút vì nó không phải xác định các cài đặt chính xác trong quá trình so sánh.

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

Ví dụ sử dụng:

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

BẢN GIỚI THIỆU


Chức năng gốc:

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

BẢN GIỚI THIỆU


2
Đối với bản ghi, chức năng này vẫn có thể được cải thiện bằng cách xử lý trước danh sách đối số và tạo một "mảng tùy chọn sắp xếp" thống nhất. Đây là phần còn lại để tập thể dục cho người đọc;)
Felix Kling

@Mike: Ok ... cuối cùng;) Bạn thấy nó phức tạp hơn bây giờ, vì các tùy chọn được xử lý trước, nhưng chức năng so sánh cuối cùng (xem bình luận) đơn giản hơn nhiều (hy vọng) dẫn đến hiệu suất tốt hơn. Bạn càng có nhiều tùy chọn sắp xếp, bạn càng có nhiều lợi thế từ phương pháp này.
Felix Kling

165

cho một giải pháp đơn giản, không chung chung cho vấn đề chính xác của bạn:

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
Tôi nghĩ bản demo này là những gì OP muốn => jsfiddle.net/zJ6UA/533
Amin Jafari

3
Điều này có ý tưởng đúng, nhưng logic là tất cả sai. Bạn không thể trừ một chuỗi không phải số từ một chuỗi khác và ifcâu lệnh không có nghĩa gì.
JLRishe

6
Bạn có thể sử dụng a.localeCompare(b)ở dòng cuối cùng để so sánh chuỗi ... xem tài liệu
Michael P

2
Không nên so sánh thành phố đầu tiên để kiểm tra sự bình đẳng, không phải bất bình đẳng? Nói cách khác, không nên là dòng if (a.city === b.city)? Nghĩa là, nếu hai thành phố giống nhau thì so sánh giá cả, nếu không thì so sánh các thành phố.
Steven Rands

2
một trong những câu trả lời hay nhất tnx.
jonathana

54

Bạn có thể sử dụng một cách tiếp cận sắp xếp chuỗi bằng cách lấy delta của các giá trị cho đến khi nó đạt đến một giá trị không bằng không.

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

Hoặc, sử dụng es6, chỉ cần:

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

16
Tui bỏ lỡ điều gì vậy? Tại sao sử dụng 60 dòng mã cho một cái gì đó có thể được thực hiện trong 1. Đơn giản, rõ ràng, súc tích. Nên là câu trả lời được chấp nhận IMO.
Erez Cohen

Một trong những vấn đề lớn hiện nay của SO là các câu trả lời cũ - thường được thay thế bằng các giải pháp tốt hơn bằng cách sử dụng các tính năng ngôn ngữ mới (ví dụ ES5-6-7) để duy trì điểm số cũ của chúng và tất cả chúng ta phải cuộn xuống để tìm ra "thực" tốt nhất các giải pháp! SO nên hết hạn phiếu theo thời gian để giải quyết vấn đề này, bởi vì vấn đề đang trở nên tồi tệ hơn khi thời gian trôi qua.
Andy Lorenz

53

Đây là một cách tiếp cận chức năng đơn giản. Chỉ định thứ tự sắp xếp sử dụng mảng. Chuẩn bị trừ để xác định thứ tự giảm dần.

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

Chỉnh sửa: trong ES6 thậm chí còn ngắn hơn!

"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
Tôi thấy chức năng này khá gọn gàng vì vậy tôi đã thực hiện một cải tiến hiệu suất nhỏ lên tới 90% tùy thuộc vào trình phân tích cú pháp. Tôi đã thực hiện một ý chínhbộ thử nghiệm .
php_nub_qq

Dựa trên các dữ liệu mẫu có vẻ như con số được sắp xếp như mong đợi, tuy nhiên khi tôi cố gắng thực hiện số này, nơi sắp xếp giống như dây ... [10,100,11,9]. Tôi đã bỏ lỡ một cái gì đó?
Mark Carpenter Jr

@MarkCarpenterJr. Không chắc chắn những gì bạn có ý nghĩa. ví dụ của tôi sắp xếp các loại số chính xác. Bạn có thể chia sẻ việc thực hiện của bạn như một câu hỏi và tham khảo tôi trong các ý kiến ​​để tôi thấy nó không? Sau đó tôi có thể kiểm tra.
chriskelly

@MarkCarpenterJr. Chỉ cần phát hiện ra nó. Tôi đã thêm một lời giải thích trong các ý kiến.
chriskelly

32

Tôi đã thực hiện một sắp xếp đa tính năng khá chung chung ngày hôm nay. Bạn có thể xem thenBy.js tại đây: https://github.com/Teun/thenBy.js

Nó cho phép bạn sử dụng Array.sort tiêu chuẩn, nhưng với kiểu FirstBy (). ThenBy (). ThenBy (). Đó là cách ít mã và phức tạp hơn các giải pháp được đăng ở trên.


8
Chà, khi bạn gọi 3 lần, cuộc gọi thứ 2 không được đảm bảo để lại thứ tự của cuộc gọi đầu tiên cho các mục mà cuộc gọi thứ hai không tạo ra sự khác biệt.
Teun D

13

Hàm sau sẽ cho phép bạn sắp xếp một mảng các đối tượng trên một hoặc nhiều thuộc tính, tăng dần (mặc định) hoặc giảm dần trên mỗi thuộc tính và cho phép bạn chọn có thực hiện so sánh trường hợp nhạy cảm hay không. Theo mặc định, chức năng này thực hiện các loại không phân biệt chữ hoa chữ thường.

Đối số đầu tiên phải là mảng chứa các đối tượng. (Các) đối số tiếp theo phải là một danh sách các chuỗi được phân tách bằng dấu phẩy tham chiếu các thuộc tính đối tượng khác nhau để sắp xếp theo. Đối số cuối cùng (là tùy chọn) là một boolean để chọn có hay không thực hiện các loại phân biệt chữ hoa chữ thường - sử dụng truecho các loại phân biệt chữ hoa chữ thường.

Hàm sẽ sắp xếp từng thuộc tính / khóa theo thứ tự tăng dần theo mặc định. Nếu bạn muốn một khóa cụ thể sắp xếp theo thứ tự giảm dần, thì thay vào đó hãy chuyển vào một mảng ở định dạng này : ['property_name', true].

Dưới đây là một số cách sử dụng mẫu của hàm theo sau là một lời giải thích (trong đó homesmột mảng chứa các đối tượng):

objSort(homes, 'city') -> sắp xếp theo thành phố (tăng dần, trường hợp nhạy cảm)

objSort(homes, ['city', true]) -> sắp xếp theo thành phố (giảm dần, trường hợp nhạy cảm)

objSort(homes, 'city', true)-> sắp xếp theo thành phố sau đó giá (tăng dần, trường hợp nhạy cảm )

objSort(homes, 'city', 'price') -> sắp xếp theo thành phố sau đó giá (cả tăng dần, trường hợp nhạy cảm)

objSort(homes, 'city', ['price', true]) -> sắp xếp theo thành phố (tăng dần) sau đó giá (giảm dần), trường hợp nhạy cảm)

Và không cần phải quảng cáo thêm, đây là chức năng:

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

Và đây là một số dữ liệu mẫu:

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

Đây là một mánh gian lận hoàn toàn nhưng tôi nghĩ rằng nó tăng thêm giá trị cho câu hỏi này vì về cơ bản nó là một chức năng thư viện đóng hộp mà bạn có thể sử dụng ngoài hộp.

Nếu mã của bạn có quyền truy cập lodashhoặc một thư viện tương thích lodash như thế underscorethì bạn có thể sử dụng _.sortByphương thức này. Đoạn mã dưới đây được sao chép trực tiếp từ tài liệu lodash .

Các kết quả được nhận xét trong các ví dụ trông giống như chúng trả về các mảng của mảng nhưng điều đó chỉ hiển thị thứ tự và không phải là kết quả thực tế là một mảng các đối tượng.

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

Đây là một cái khác có lẽ gần với ý tưởng của bạn hơn cho cú pháp

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

Bản trình diễn: http://jsfiddle.net/Nq4dk/2/


Chỉnh sửa: Để giải trí, đây là một biến thể chỉ cần một chuỗi giống như sql, vì vậy bạn có thể làmsortObjects(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);
    });
}

giải pháp này là sạch nhưng không hiệu quả vì so sánh mảng. bạn có thể chỉ cần xem qua các thuộc tính theo dõi giá trị được so sánh và nó không bằng 0, trả về. đó là nhanh hơn rất nhiều.
amankapur91

4

Đơn giản hơn một:

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

3

Tôi thích cách tiếp cận của SnowBurnt nhưng nó cần một tinh chỉnh để kiểm tra sự tương đương trong thành phố KHÔNG phải là một sự khác biệt.

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

3

Đây là một loại đa chiều chung, cho phép đảo ngược và / hoặc ánh xạ trên mỗi cấp.

Viết bằng bản in. Đối với Javascript, hãy xem JSFiddle này

Mật mã

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

Ví dụ sử dụng

Sắp xếp một mảng người theo tên cuối cùng, sau đó tên:

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

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

Sắp xếp mã ngôn ngữ theo tên của họ , không phải mã ngôn ngữ của họ (xem map), sau đó theo phiên bản giảm dần (xem 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,
  }
]));

2

Một cách năng động để làm điều đó với các phím NHIỀU:

  • lọc các giá trị duy nhất từ ​​mỗi col / khóa sắp xếp
  • đặt theo thứ tự hoặc đảo ngược nó
  • thêm trọng số zeropad cho mỗi đối tượng dựa trên các giá trị khóa indexOf (value)
  • sắp xếp sử dụng trọng lượng caclutated

nhập mô tả hình ảnh ở đây

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

Sử dụng:

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

nhập mô tả hình ảnh ở đây


1

Đây là phiên bản chung của giải pháp @ 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;
        }
    }
}


})

Điều này dựa trên một thói quen sắp xếp mà tôi đang sử dụng. Tôi đã không kiểm tra mã cụ thể này để nó có thể có lỗi nhưng bạn hiểu ý. Ý tưởng là sắp xếp dựa trên trường đầu tiên chỉ ra sự khác biệt và sau đó dừng lại và đi đến bản ghi tiếp theo. Vì vậy, nếu bạn sắp xếp theo ba trường và trường đầu tiên trong so sánh là đủ để xác định thứ tự sắp xếp của hai bản ghi được sắp xếp thì trả về kết quả sắp xếp đó và chuyển đến bản ghi tiếp theo.

Tôi đã thử nghiệm nó (thực sự với logic sắp xếp phức tạp hơn một chút) trên 5000 bản ghi và nó đã làm điều đó trong chớp mắt. Nếu bạn thực sự đang tải hơn 1000 bản ghi cho khách hàng, có lẽ bạn nên sử dụng phân loại và lọc phía sever.

Mã này không xử lý phân biệt chữ hoa chữ thường nhưng tôi để nó cho người đọc xử lý sửa đổi nhỏ này.


1

Đây là giải pháp của tôi dựa trên thành ngữ biến đổi Schwartzian , hy vọng bạn thấy nó hữu ích.

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

Đây là một ví dụ về cách sử dụng nó:

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
Tôi đã thử một vài điều về điều này (và các trang khác). Giải pháp này của a8m chỉ là một cách phù hợp với tình huống của tôi: gist.github.com/cemerson/f1f1434286c1262b403f3d85c96688e0
Christopher D. Emerson

1

Cách khác

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

Cách sử dụng (đặt - (trừ) trước trường nếu bạn muốn sắp xếp theo thứ tự trường giảm dần)

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

Sử dụng hàm trên, bạn có thể sắp xếp bất kỳ mảng json nào với nhiều trường. Không cần thay đổi cơ thể chức năng nào cả


0

Điều chỉnh câu trả lời của @chriskelly.


Hầu hết các câu trả lời bỏ qua rằng giá sẽ không sắp xếp đúng nếu giá trị nằm trong mười nghìn và thấp hơn hoặc hơn một triệu. Resaon được sắp xếp theo thứ tự abc. Nó đã được trả lời khá tốt ở đây, Tại sao JavaScript không thể sắp xếp "5, 10, 1" và ở đây Cách sắp xếp một mảng các số nguyên một cách chính xác .

Cuối cùng, chúng ta phải thực hiện một số đánh giá nếu trường hoặc nút mà chúng ta sắp xếp là một số. Tôi không nói rằng sử dụng parseInt()trong trường hợp này là câu trả lời chính xác, kết quả được sắp xếp là quan trọng hơn.

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>


Một bí quyết để kiểm tra với


Vấn đề là với dữ liệu bạn đang cố gắng sắp xếp. pricetrong ví dụ là ở định dạng chuỗi. Nếu bạn muốn nó hoạt động chính xác với ví dụ của tôi, hãy sử dụng bản đồ để chuyển đổi trường bạn muốn định dạng số trước. tức làconst correctedHomes = homes.map(h => ({...h, price: +h.price}))
chriskelly

0

Wow, có một số giải pháp phức tạp ở đây. Quá phức tạp, tôi quyết định nghĩ ra một thứ đơn giản hơn nhưng cũng khá mạnh mẽ. Nó đây rồi;

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

Và đây là một ví dụ về cách bạn sử dụng nó.

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

Điều này trước tiên sẽ sắp xếp theo mức độ ưu tiên của các thuộc tính, sau đó theo giá trị của các thuộc tính.


0

Đây là một cách mở rộng để sắp xếp theo nhiều lĩnh vực.

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

Ghi chú

  • a.localeCompare(b)được phổ biến được hỗ trợ và lợi nhuận -1,0,1 nếu a<b, a==b,a>b tương ứng.
  • Phép trừ hoạt động trên các trường số.
  • ||trong dòng cuối cùng cityưu tiên hơnprice .
  • Phủ định để đảo ngược thứ tự trong bất kỳ lĩnh vực nào, như trong -price_order
  • So sánh ngày , var date_order = new Date(left.date) - new Date(right.date);hoạt động như số vì toán ngày biến thành mili giây kể từ năm 1970.
  • Thêm các trường vào chuỗi hoặc return city_order || -price_order || date_order;

0

Tôi nghĩ rằng đây có thể là cách dễ nhất để làm điều đó.

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

Nó thực sự đơn giản và tôi đã thử nó với 3 cặp giá trị khóa khác nhau và nó hoạt động rất tốt.

Đây là một ví dụ đơn giản, nhìn vào liên kết để biết thêm chi tiết

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

Đây là của tôi để bạn tham khảo, với ví dụ:

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

Tôi đã tìm kiếm một cái gì đó tương tự và kết thúc với điều này:

Đầu tiên chúng ta có một hoặc nhiều hàm sắp xếp, luôn trả về 0, 1 hoặc -1:

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

Bạn có thể tạo thêm chức năng cho từng thuộc tính khác mà bạn muốn sắp xếp.

Sau đó, tôi có một chức năng kết hợp các chức năng sắp xếp này thành một:

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

Điều này có thể được sử dụng để kết hợp các chức năng sắp xếp ở trên một cách dễ đọc:

const sorter = createSorter(sortByTitle, sortByYear)

items.sort(sorter)

Khi một hàm sắp xếp trả về 0, hàm sắp xếp tiếp theo sẽ được gọi để sắp xếp thêm.


0

Chỉ là một lựa chọn khác. Xem xét sử dụng chức năng tiện ích sau:

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

Ví dụ về cách sử dụng (trong trường hợp của bạn):

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

Cần lưu ý rằng chức năng này có thể được khái quát hơn nữa để có thể sử dụng các thuộc tính lồng nhau như 'address.city' hoặc 'style.size. Thong', v.v.


0

Đây là một thuật toán đệ quy để sắp xếp theo nhiều trường trong khi có cơ hội định dạng các giá trị trước khi so sánh.

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

Nếu a và b bằng nhau, nó sẽ chỉ thử trường tiếp theo cho đến khi không có sẵn.


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

Tại sao không đặt mọi thứ vào một chức năng duy nhất? Nếu thành phố không bằng nhau, trả lại diff của chúng, khác, khác giá.
Nhà vật lý điên

-1

Ở đây 'AffiliateDueDate' và 'Title' là các cột, cả hai đều được sắp xếp theo thứ tự tăng dần.

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

Sắp xếp trên hai trường ngày và ví dụ trường số:

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

Sử dụng:

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


-1

Làm thế nào về giải pháp đơn giản này:

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 
}

Dựa trên câu hỏi này sắp xếp mảng javascript theo nhiều trường (số)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.