Câu hỏi Sắp xếp mảng các đối tượng theo giá trị thuộc tính chuỗi trong JavaScript


Tôi có một mảng các đối tượng JavaScript:

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

Làm thế nào tôi có thể sắp xếp chúng theo giá trị của last_nom trong JavaScript?

Tôi biết về sort(a,b), nhưng điều đó dường như chỉ hoạt động trên dây và số. Tôi có cần thêm phương thức toString vào đối tượng của mình không?


1829
2017-07-15 03:17


gốc


Kịch bản này cho phép bạn làm điều đó trừ khi bạn muốn viết chức năng so sánh hoặc sắp xếp của riêng bạn: thomasfrank.se/sorting_things.html - Baversjo


Các câu trả lời:


Thật dễ dàng để viết chức năng so sánh của riêng bạn:

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

objs.sort(compare);

Hoặc nội tuyến (c / o Marco Demaio):

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

2716
2017-07-15 03:35



Hoặc nội tuyến: objs.sort (hàm (a, b) {return (a.last_nom> b.last_nom)? 1: ((b.last_nom> a.last_nom)? -1: 0);}); - Marco Demaio
Tài liệu chính thức: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… - mikemaccana
return a.last_nom.localeCompare(b.last_nom) cũng sẽ hoạt động. - Cerbrus
đối với những người đang tìm kiếm một loại nơi trường là số, thì hàm so sánh của hàm: return a.value - b.value; (ASC) - Andre Figueiredo
@Cerbrus localeCompare là điều quan trọng khi sử dụng các ký tự có dấu trọng âm trong tiếng nước ngoài và thanh lịch hơn. - Marcos Lima


Bạn cũng có thể tạo một hàm sắp xếp động sắp xếp các đối tượng theo giá trị của chúng mà bạn vượt qua:

function dynamicSort(property) {
    var sortOrder = 1;
    if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a,b) {
        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result * sortOrder;
    }
}

Vì vậy, bạn có thể có một mảng các đối tượng như thế này:

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

... và nó sẽ hoạt động khi bạn làm:

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

Trên thực tế điều này đã trả lời câu hỏi. Phần dưới đây được viết bởi vì nhiều người liên lạc với tôi, phàn nàn rằng nó không hoạt động với nhiều tham số.

Nhiều tham số

Bạn có thể sử dụng hàm dưới đây để tạo các hàm sắp xếp với nhiều tham số sắp xếp.

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

Mà sẽ cho phép bạn làm một cái gì đó như thế này:

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

Thêm nó vào mẫu thử nghiệm

(Thực hiện dưới đây được lấy cảm hứng từ Mike R'S câu trả lời)

Tôi sẽ không khuyên bạn nên thay đổi một nguyên mẫu đối tượng gốc nhưng chỉ để đưa ra một ví dụ để bạn có thể thực hiện nó trên các đối tượng của riêng bạn (Đối với các môi trường hỗ trợ nó, bạn cũng có thể sử dụng Object.defineProperty như được trình bày trong phần tiếp theo, ít nhất không có tác dụng phụ tiêu cực của việc liệt kê, như được mô tả ở phần cuối)

Việc triển khai thử nghiệm sẽ giống như sau (Đây là một ví dụ làm việc):

//Don't just copy-paste this code. You will break the "for-in" loops
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Array.prototype.sortBy = function() {
        return this.sort(_dynamicSortMultiple.apply(null, arguments));
    }
}();

Cách "OK" của việc thêm nó vào mẫu thử nghiệm

Nếu bạn đang nhắm mục tiêu IE v9.0 trở lên thì như tôi đã đề cập trước đây, hãy sử dụng Object.defineProperty như thế này (ví dụ làm việc):

//Won't work below IE9, but totally safe otherwise
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Object.defineProperty(Array.prototype, "sortBy", {
        enumerable: false,
        writable: true,
        value: function() {
            return this.sort(_dynamicSortMultiple.apply(null, arguments));
        }
    });
}();

Đây có thể là một thỏa hiệp chấp nhận được cho đến khi toán tử liên kết đến.

Tất cả những niềm vui nguyên mẫu cho phép điều này:

People.sortBy("Name", "-Surname");

Bạn nên đọc điều này

Nếu bạn sử dụng phương pháp truy cập nguyên mẫu trực tiếp (Object.defineProperty là tốt) và mã khác không kiểm tra hasOwnProperty, mèo con chết! Ok, thành thật mà nói, không có tổn hại nào xảy ra với bất kỳ con mèo con nào nhưng có lẽ mọi thứ sẽ bị phá vỡ và mọi nhà phát triển khác trong nhóm của bạn sẽ ghét bạn:

evil

Xem "SortBy" cuối cùng? Ừ. Không mát mẻ. Sử dụng Object.defineProperty nơi bạn có thể, và để lại Array.prototype một mình nếu không.


650
2018-01-21 15:03



Xin lưu ý rằng tên thuộc tính trong JavaScript có thể là bất kỳ chuỗi nào và nếu bạn có thuộc tính bắt đầu bằng "-" (rất khó xảy ra và có lẽ không phải là một ý tưởng tốt), bạn sẽ cần phải sửa đổi các chức năng dynamicSort để sử dụng một cái gì đó khác như một chỉ số sắp xếp đảo ngược. - Ege Özcan
Tôi đã nhận thấy rằng dynamicSort() trong ví dụ trên sẽ đặt các chữ cái viết hoa lên trước chữ thường. Ví dụ, nếu tôi có các giá trị APd, Aklinvà Abe - kết quả trong một loại ASC nên Abe, Aklin, APd. Nhưng với ví dụ của bạn, kết quả là APd, Abe, Aklin. Dù sao để sửa chữa hành vi này? - Lloyd Banks
@LloydBanks nếu bạn đang sử dụng nghiêm ngặt cho các chuỗi, sau đó bạn có thể sử dụng var result = a[property].localeCompare(b[property]); thay vì var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;. - Ege Özcan
Giống như câu trả lời hàng đầu, câu trả lời này không thành công trong trường hợp tồn tại các thuộc tính không xác định: [ { a: 5 }, {a:2}, {}, {a:1} ] - dzh
@ EgeÖzcan Nó hoặc là nên đặt các mục vào đầu hoặc cuối, đây là thực hiện tôi đã kết thúc bằng cách sử dụng pastebin.com/g4dt23jU - dzh


underscore.js

sử dụng gạch dưới, nhỏ và tuyệt vời của nó ...

sortBy_.sortBy (danh sách, vòng lặp, [bối cảnh]) Trả về một bản sao đã sắp xếp của   danh sách, được xếp hạng theo thứ tự tăng dần theo kết quả chạy từng giá trị   thông qua trình lặp. Iterator cũng có thể là tên chuỗi của tài sản   để sắp xếp theo (ví dụ: độ dài).

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

var sortedObjs = _.sortBy( objs, 'first_nom' );

155
2018-05-10 21:24



David, bạn có thể chỉnh sửa câu trả lời để nói, var sortedObjs = _.sortBy( objs, 'first_nom' );. objs sẽ không phải được sắp xếp chính nó như là kết quả của việc này. Chức năng sẽ trở về một mảng được sắp xếp. Điều đó sẽ làm cho nó rõ ràng hơn. - Jess
Để đảo ngược sắp xếp: var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse(); - Erdal G.
bạn cần tải thư viện javascript "gạch dưới": <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"> </script> - and-bri
Cũng có sẵn trong Lodash cho những người thích cái đó - WoJ


Đừng hiểu tại sao mọi người làm cho nó phức tạp như vậy:

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

Đối với động cơ nghiêm ngặt hơn:

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

Hoán đổi toán tử để nó được sắp xếp theo thứ tự bảng chữ cái ngược.


141
2018-01-24 19:35



Điều này thực sự không đúng vì hàm được sử dụng trong sắp xếp phải trả về -1, 0 hoặc 1 nhưng hàm trên trả về một boolean. Các loại hoạt động tốt trong chrome nhưng không thành công trong PhantomJS ví dụ. Xem code.google.com/p/phantomjs/issues/detail?id=1090 - schup
Một số động cơ tài khoản cho ngu dốt, cách này là loại khai thác đó. Tôi đã cập nhật câu trả lời của mình với phiên bản phù hợp. - p3lim
Tôi muốn đề nghị chỉnh sửa để đưa ra phiên bản đầu tiên. Nó gọn gàng hơn nên trông hấp dẫn hơn, nhưng nó không hoạt động, ít nhất là không đáng tin cậy. Nếu ai đó cố gắng trong một trình duyệt và nó hoạt động, họ thậm chí có thể không nhận ra rằng họ có một vấn đề (đặc biệt là nếu họ không đọc các bình luận). Phiên bản thứ hai hoạt động đúng cách nên thực sự không cần thiết cho phiên bản đầu tiên. - Kate
Có phiên bản đầu tiên có nhiều tham số hay không - cụ thể là tìm kiếm sắp xếp theo số lượng rồi theo thứ tự bảng chữ cái - Lion789
@Simon Tôi thực sự đánh giá cao có phiên bản "hơi sai" đầu tiên, kể từ khi thực hiện nghiêm ngặt mất một vài giây để phân tích cú pháp và hiểu và sẽ khó khăn hơn nhiều mà không có nó. - Aaron Sherman


Trong ES6 / ES2015 hoặc mới hơn, bạn có thể thực hiện theo cách này:

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

138
2018-01-29 19:44



điều này đã có sẵn kể từ JS 1.1, mảnh mũi tên béo này là phần ES6 / 2015. Nhưng vẫn rất hữu ích và trả lời tốt nhất theo ý kiến ​​của tôi - Jon Harding
@PratikKelwalkar: nếu bạn cần đảo ngược nó chỉ cần chuyển đổi a và b so sánh: objs.sort ((a, b) => b.last_nom.localeCompare (a.last_nom)); - Vlad Bezden
cũng có thể sử dụng chỉ mục để xử lý trường để sắp xếp: thay vì last_nom chỉ sử dụng số trong mảng: 1 ? - and-bri
@VladBezden cảm ơn câu trả lời của bạn! Giải pháp này là giải pháp đầu tiên với nỗ lực lập trình rất nhỏ và kết quả sắp xếp chính xác và một mảng chuỗi như: ["Name1", "Name10", "Name2", "something else", "Name11"]. Tôi đã sắp xếp để hoạt động chính xác với objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom, undefined, {numberic: true})); - scipper


Nếu bạn có trùng lặp họ, bạn có thể sắp xếp chúng theo tên đầu tiên-

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

51
2017-07-15 04:03



Cái đó a< b  a >b định dạng thú vị. - Dodekeract


Giải pháp đơn giản và nhanh chóng cho vấn đề này khi sử dụng thừa kế nguyên mẫu:

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

Ví dụ / Cách sử dụng

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

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

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

Cập nhật: Không còn sửa đổi mảng gốc.


38
2017-07-10 11:54



Nó không chỉ trả về một mảng khác. nhưng thực sự sắp xếp bản gốc!. - Vinay Aggarwal
Nếu bạn muốn đảm bảo rằng bạn đang sử dụng sắp xếp tự nhiên với các số (tức là 0,1,2,10,11 vv ...), hãy sử dụng parseInt với tập hợp Radix. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… như vậy: return (parseInt (a [p], 10)> parseInt (b [p], 10))? 1: (parseInt (a [p], 10) <parseInt (b [p], 10))? -1: 0; - Paul
@codehuntr Cảm ơn bạn đã sửa nó. nhưng tôi đoán thay vì làm cho chức năng sắp xếp để làm điều này nhạy cảm, nó tốt hơn nếu chúng ta thực hiện một chức năng riêng biệt để sửa chữa các loại dữ liệu. Vì hàm sắp xếp không thể cho biết thuộc tính nào sẽ chứa loại dữ liệu nào. :) - Vinay Aggarwal
Rất đẹp. Đảo ngược các mũi tên để có được hiệu ứng asc / desc. - Abdul Sadik Yalcin