Sắp xếp mảng các đối tượng theo giá trị thuộc tính chuỗi


2763

Tôi có một loạt 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 cách nào tôi có thể sắp xếp chúng theo giá trị của last_nomJavaScript?

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


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

cách nhanh nhất là sử dụng mô đun mảng sắp xếp đẳng cấu , hoạt động tự nhiên trong cả trình duyệt và nút, hỗ trợ mọi loại đầu vào, trường được tính toán và thứ tự sắp xếp tùy chỉnh.
Lloyd

Câu trả lời:


3922

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((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0)); 

441
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


176
return a.last_nom.localeCompare(b.last_nom)cũng sẽ làm việc
Cerbrus

146
đối với những người tìm kiếm một loại trong đó trường là số, thân hàm so sánh: return a.value - b.value;(ASC)
Andre Figueiredo

20
@Cerbrus localeComparerất quan trọng khi sử dụng các ký tự có dấu trong tiếng nước ngoài và cũng thanh lịch hơn.
Marcos Lima

839

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) {
        /* next line works with strings and numbers, 
         * and you may want to customize it to your needs
         */
        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result * sortOrder;
    }
}

Vì vậy, bạn có thể có một loạt 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 bên dưới được viết vì nhiều người liên hệ 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 bên dưới để 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;
    }
}

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

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

Mảng phân lớp

Đối với những người may mắn trong số chúng ta có thể sử dụng ES6, cho phép mở rộng các đối tượng bản địa:

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

Điều đó sẽ cho phép điều này:

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

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ó các thuộc tính bắt đầu bằng dấu "-" (rất khó xảy ra và có thể không phải là ý tưởng hay), bạn sẽ cần sửa đổi hàm DynamicSort để sử dụng một thứ khác làm sắp xếp ngược lại chỉ tiêu.
Ege Özcan

Tôi đã nhận thấy rằng dynamicSort()trong ví dụ trên sẽ đặt chữ in hoa trước chữ in thường. Ví dụ, nếu tôi có giá trị APd, AklinAbe- kết quả trong một ASC loại 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 hành vi này?
Lloyd Banks

6
@LloydBanks nếu bạn đang sử dụng điều này một cách nghiêm ngặt cho chuỗi, thì 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

1
@ EgeÖzcan Bạn nên đặt các mục lên trên cùng hoặc dưới cùng, đây là triển khai Tôi đã kết thúc bằng pastebin.com/g4dt23jU
dzh

1
Đây là giải pháp tuyệt vời nhưng có một vấn đề nếu bạn so sánh các con số. Vui lòng thêm phần này trước khi kiểm tra:if( !isNaN(a[property]) ) a[property] = Number(a[property]); if( !isNaN(b[property]) ) b[property] = Number(b[property]);
Ivijan Stefan Stipić

416

Trong ES6 / ES2015 trở lê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));

Trước ES6 / ES2015

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

29
cái này đã có sẵn kể từ JS 1.1, mảnh mũi tên béo này là mảnh ES6 / 2015. Nhưng vẫn rất hữu ích, và câu trả lời tốt nhất theo ý kiến ​​của tôi
Jon Harding

13
@PratikKelwalkar: nếu bạn cần đảo ngược, chỉ cần chuyển đổi so sánh a và b: objs.sort ((a, b) => b.last_nom.localeCompare (a.last_nom));
Vlad Bezden

có thể sử dụng một chỉ mục để giải quyết trường để sắp xếp: thay vì last_nomchỉ sử dụng số trong mảng : 1?
và-bri

4
@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 chuỗi chuỗi như: ["Name1", "Name10", "Name2", "cái gì khác", "Name11"]. Tôi đã sắp xếp để làm việc chính xác vớiobjs.sort((a, b) => a.last_nom.localeCompare(b.last_nom, undefined, {numberic: true}));
scipper

1
Để làm điều này với các số giảm dần: .sort ((a, b) => b.numberProperty - a.numberProperty). Tăng dần: .sort ((a, b) => a.numberProperty - b.numberProperty)
Sebastian Patten

188

gạch dưới

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

sortBy_.sortBy (list, iterator, [bối cảnh]) Trả về một bản sao được sắp xếp của danh sách, được xếp theo thứ tự tăng dần theo kết quả chạy từng giá trị thông qua iterator. Iterator cũng có thể là tên chuỗi của thuộc tính để 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' );

18
David, bạn có thể chỉnh sửa câu trả lời để nói , var sortedObjs = _.sortBy( objs, 'first_nom' );. objssẽ không được tự sắp xếp như là kết quả của việc này. Hàm sẽ trả về một mảng đã sắp xếp. Điều đó sẽ làm cho nó rõ ràng hơn.
Jess

10
Để đảo ngược sắp xếp:var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse();
Erdal G.

2
bạn cần tải "javers" libary libary:<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"> </script>
và-bri

5
Cũng có sẵn Lodashcho những người thích cái đó
WoJ

2
Trong lodash, điều này sẽ giống nhau: var sortedObjs = _.sortBy( objs, 'first_nom' );hoặc nếu bạn muốn nó theo một thứ tự khác:var sortedObjs = _.orderBy( objs, ['first_nom'],['dsc'] );
Travis Heeter 15/11/18

186

Đừng hiểu tại sao mọi người lại làm nó quá phức tạp:

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

Đối với động cơ chặt chẽ hơn:

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

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


17
Điều này thực sự không đúng vì hàm được sử dụng trong sắp xếp sẽ trả về -1, 0 hoặc 1 nhưng hàm trên trả về giá trị boolean. Loại này hoạt động tốt trong chrome nhưng không thành công trong PhantomJS. Xem code.google.com/p/ph
Phantomjs/issues/detail?id=1090

Một số động cơ cho sự ngu ngốc, cách này là loại khai thác đó. Tôi đã cập nhật trả lời của tôi với một phiên bản thích hợp.
p3lim

19
Tôi khuyên bạn nên chỉnh sửa để đưa ra phiên bản đầu tiên. Nó ngắn gọn 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 đó thử nó 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ó 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, vì vậy thực sự không cần phải có phiên bản đầu tiên.
Kate

2
@Simon Tôi thực sự đánh giá cao việc có phiên bản "hơi sai" trước tiên, vì việc triển khai chặt chẽ hơn mất vài giây để phân tích và hiểu và sẽ khó hơn nhiều nếu không có nó.
Aaron Sherman

1
@ Lion789 chỉ cần làm điều này:if(a.count == b.count) return a.name > b.name; else return a.count > b.count;
p3lim

62

Nếu bạn có tên trùng lặp, bạn có thể sắp xếp chúng theo tê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;
});

@BadFeeling AboutĐiều này trả về -1 hoặc 1 có nghĩa là gì? Tôi hiểu rằng -1 theo nghĩa đen có nghĩa là A nhỏ hơn B chỉ bằng cú pháp, nhưng tại sao lại sử dụng 1 hoặc -1? Tôi thấy mọi người đang sử dụng những con số đó làm giá trị trả về, nhưng tại sao? Cảm ơn.
Chris22

1
@ Chris22 một số âm được trả về có nghĩa là bsẽ xuất hiện sau amảng. Nếu một số dương được trả về, nó có nghĩa là anên đến sau b. Nếu 0được trả lại, có nghĩa là chúng được coi là bằng nhau. Bạn luôn có thể đọc tài liệu: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
gợi

@BadFeeling About This xin cảm ơn, đã giải thích và liên kết. Dù bạn có tin hay không, tôi đã tìm ra nhiều đoạn mã khác nhau bằng cách sử dụng 1, 0, -1trước khi tôi hỏi điều này ở đây. Tôi chỉ không tìm thấy thông tin tôi cần.
Chris22

48

Giải pháp đơn giản và nhanh chóng cho vấn đề này bằng cách sử dụng kế thừa 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 ban đầu.


5
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ố (ví dụ: 0,1,2,10,11, v.v.), hãy sử dụng parseInt với bộ Radix. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ Khăn so: 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ì thực hiện chức năng sắp xếp để thực hiện việc nhạy cảm này, sẽ tốt hơn nếu chúng ta tạo một chức năng riêng để sửa các kiểu dữ liệu. Bởi vì chức năng 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

4
không bao giờ, không bao giờ đề xuất một giải pháp sửa đổi nguyên mẫu của một đối tượng cốt lõi
pbanka

39

Tính đến năm 2018 có một giải pháp ngắn gọn và thanh lịch hơn nhiều. Chỉ dùng. Array.prototype.sort () .

Thí dụ:

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

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

7
Trong các chuỗi câu hỏi đã được sử dụng để so sánh với các con số. Câu trả lời của bạn hoạt động rất tốt để sắp xếp theo số, nhưng không tốt cho việc so sánh theo chuỗi.
smcstewart

Việc a.value - b.valuesử dụng để so sánh các thuộc tính của đối tượng ( số trong trường hợp này) có thể được chấp nhận cho các thời điểm khác nhau của dữ liệu. Ví dụ, regex có thể được sử dụng để so sánh từng cặp chuỗi lân cận .
0leg

1
@ 0leg Tôi muốn xem một ví dụ ở đây về việc sử dụng regex để so sánh các chuỗi theo cách này.
Bob Stein

Việc thực hiện này là khá tốt nếu bạn cần sắp xếp nó theo ID. Vâng, bạn đã đề nghị sử dụng regex để so sánh chuỗi lân cận, điều này làm cho giải pháp trở nên phức tạp hơn trong khi mục đích của phiên bản đơn giản này sẽ khác nếu regex được sử dụng cùng với giải pháp đã cho. Đơn giản là tốt nhất.
Surendrapanday

33

Câu trả lời cũ không đúng:

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

CẬP NHẬT

Từ bình luận của Beauchamp:

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

Định dạng dễ đọc hơn:

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

Không có chim nhạn lồng nhau:

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

Giải thích: Number()sẽ đúc trueđến 1falseđến 0.


Nó hoạt động, nhưng kết quả không ổn định vì một số lý do
TitanFighter

@ AO17 không nó sẽ không. Bạn không thể trừ các chuỗi.
Patrick Roberts

15
Điều này nên làm điều đó:arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))
Jean-François Beauchamp

3
@ Jean-FrançoisBeauchamp, giải pháp của bạn hoạt động hoàn toàn tốt và tốt hơn nhiều.
Ahsan

cái thứ 3 với Number rất đơn giản và tốt đẹp!
Kojo

29

Thay vì sử dụng chức năng so sánh tùy chỉnh, bạn cũng có thể tạo một loại đối tượng bằng toString()phương thức tùy chỉnh (được gọi bởi chức năng so sánh mặc định):

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

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

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

29

Lodash.js (thay thế của Underscore.js )

Thật tốt khi không thêm một khung cho mọi logic đơn giản, nhưng dựa vào các khung tiện ích được kiểm tra tốt có thể tăng tốc độ phát triển và giảm số lượng lỗi.

Lodash tạo mã rất sạch và thúc đẩy một phong cách lập trình chức năng hơn . Trong một thoáng, nó trở nên rõ ràng ý định của mã là gì.

Vấn đề của OP có thể được giải quyết đơn giản là:

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

Thêm thông tin? Ví dụ: chúng ta có đối tượng lồng nhau sau:

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

Bây giờ chúng ta có thể sử dụng tốc_.propertyuser.age để chỉ định đường dẫn đến thuộc tính cần khớp. Chúng tôi sẽ sắp xếp các đối tượng người dùng theo thuộc tính tuổi lồng nhau. Có, nó cho phép khớp tài sản lồng nhau!

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

Muốn nó đảo ngược? Không vấn đề gì. Sử dụng _.reverse .

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

Bạn muốn kết hợp cả hai sử dụng chuỗi ?

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

Hoặc khi nào bạn thích chảy qua chuỗi

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

28

Bạn có thể dùng

Cách dễ nhất: Lodash

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

Phương thức này giống như _.sortBy ngoại trừ việc nó cho phép chỉ định các thứ tự sắp xếp của các lần lặp để sắp xếp theo. Nếu các đơn đặt hàng không được chỉ định, tất cả các giá trị được sắp xếp theo thứ tự tăng dần. Mặt khác, chỉ định một thứ tự "giảm dần" cho giảm dần hoặc "asc" cho thứ tự sắp xếp tăng dần của các giá trị tương ứng.

Tranh luận

bộ sưu tập (Array | Object): Bộ sưu tập lặp lại. [iteratees = [_. sắc]] (Mảng [] | Hàm [] | Object [] | chuỗi []): Các vòng lặp để sắp xếp theo. [order] (string []): Các thứ tự sắp xếp của iteratees.

Trả về

(Mảng): Trả về mảng đã sắp xếp mới.


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

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

23

Có nhiều câu trả lời hay ở đây, nhưng tôi muốn chỉ ra rằng chúng có thể được mở rộng rất đơn giản để đạt được sự sắp xếp phức tạp hơn rất nhiều. Điều duy nhất bạn phải làm là sử dụng toán tử OR để chuỗi các hàm so sánh như thế này:

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

Trong đó fn1, fn2... là các hàm sắp xếp trả về [-1,0,1]. Điều này dẫn đến việc "sắp xếp theo fn1", "sắp xếp theo fn2" tương đương với ORDER BY trong SQL.

Giải pháp này dựa trên hành vi của ||toán tử đánh giá biểu thức được đánh giá đầu tiên có thể được chuyển đổi thành true .

Dạng đơn giản nhất chỉ có một hàm nội tuyến như thế này:

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

Có hai bước với last_nom, first_nomthứ tự sắp xếp sẽ như thế này:

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

Một chức năng so sánh chung có thể là một cái gì đó như thế này:

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

Hàm này có thể được mở rộng để hỗ trợ các trường số, độ nhạy trường hợp, kiểu dữ liệu tùy ý, v.v.

Bạn có thể sử dụng nó với chuỗi ưu tiên sắp xếp:

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

Vấn đề ở đây là JavaScript thuần túy với cách tiếp cận chức năng có thể đưa bạn đi một chặng đường dài mà không cần các thư viện bên ngoài hoặc mã phức tạp. Nó cũng rất hiệu quả, vì không phải phân tích cú pháp chuỗi


23

Cách sử dụng ví dụ:

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

Kịch bản:

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

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

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

  /**
   * @param  {*} a
   * @param  {*} b
   * @return {Number}
   */
  return (a, b) => {
    if (a[key] < b[key]) {
      return moveSmaller;
    }
    if (a[key] > b[key]) {
      return moveLarger;
    }
    return 0;
  };
};

cảm ơn bạn đã phá vỡ điều này, tôi đang cố gắng hiểu tại sao các chữ số 1, 0, -1được sử dụng để sắp xếp thứ tự. Ngay cả với lời giải thích của bạn ở trên, có vẻ rất tốt-- Tôi vẫn không hoàn toàn hiểu nó. Tôi luôn nghĩ về -1việc sử dụng thuộc tính độ dài mảng, nghĩa là: arr.length = -1có nghĩa là mục đó không được tìm thấy. Có lẽ tôi đang trộn mọi thứ ở đây, nhưng bạn có thể giúp tôi hiểu tại sao chữ số 1, 0, -1được sử dụng để xác định thứ tự không? Cảm ơn.
Chris22

1
Điều này không hoàn toàn chính xác nhưng có thể giúp nghĩ về nó như thế này: hàm được truyền cho Array.sort được gọi một lần cho mỗi mục trong mảng, như đối số có tên "a". Giá trị trả về của mỗi lệnh gọi hàm là cách thay đổi chỉ mục (số vị trí hiện tại) của mục "a" so với mục tiếp theo "b". Chỉ mục ra lệnh thứ tự của mảng (0, 1, 2, v.v.) Vì vậy, nếu "a" ở chỉ số 5 và bạn trả về -1 thì 5 + -1 == 4 (di chuyển đến gần phía trước) 5 + 0 == 5 (giữ nguyên vị trí của nó) v.v ... Nó đi theo mảng so sánh 2 hàng xóm mỗi lần cho đến khi kết thúc, để lại một mảng được sắp xếp.
Jamie Mason

cảm ơn bạn đã dành thời gian để giải thích điều này hơn nữa. Vì vậy, bằng cách sử dụng lời giải thích của bạn và MDN Array.prototype.sort , tôi sẽ cho bạn biết tôi đang nghĩ gì về điều này: so với ab, nếu alớn hơn bthêm 1 vào chỉ mục avà đặt nó phía sau b, nếu anhỏ hơn b, trừ 1 từ avà đặt nó ở phía trước b. Nếu abgiống nhau, thêm 0 vào avà để nguyên như vậy.
Chris22

22

Tôi chưa thấy cách tiếp cận cụ thể này được đề xuất, vì vậy đây là một phương pháp so sánh ngắn gọn mà tôi muốn sử dụng cho cả hai stringnumber:

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

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

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

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

Đây là một lời giải thích về sortBy():

sortBy()chấp nhận một fnlựa chọn giá trị nào từ một đối tượng để sử dụng làm so sánh và trả về một hàm có thể được truyền trực tiếp tới Array.prototype.sort(). Trong ví dụ này, chúng tôi đang sử dụng o.last_nomlàm giá trị để so sánh, vì vậy bất cứ khi nào chúng tôi nhận được hai đối tượng thông qua Array.prototype.sort()như

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

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

chúng tôi sử dụng

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

để so sánh chúng.

Hãy nhớ rằng fn = o => o.last_nom, chúng ta có thể mở rộng chức năng so sánh tương đương

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

||Toán tử OR logic có chức năng đoản mạch rất hữu ích ở đây. Do cách thức hoạt động của nó, phần thân của hàm trên có nghĩa là

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

Là một phần thưởng bổ sung, đây là phần tương đương trong ECMAScript 5 không có chức năng mũi tên, thật không may là dài dòng hơn:

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

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

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

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


17

Tôi biết câu hỏi này quá cũ, nhưng tôi không thấy bất kỳ triển khai nào tương tự như của tôi.
Phiên bản này dựa trên thành ngữ biến đổi Schwartzian .

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

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

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

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

14

Sắp xếp (thêm) Mảng phức tạp của các đối tượng

Vì bạn có thể gặp các cấu trúc dữ liệu phức tạp hơn như mảng này, tôi sẽ mở rộng giải pháp.

TL; DR

Là phiên bản dễ cắm hơn dựa trên câu trả lời rất đáng yêu của @ ege-Özcan .

Vấn đề

Tôi đã gặp những điều dưới đây và không thể thay đổi nó. Tôi cũng không muốn làm phẳng đối tượng tạm thời. Tôi cũng không muốn sử dụng dấu gạch dưới / lodash, chủ yếu là vì lý do hiệu suất và niềm vui để tự thực hiện nó.

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

Mục tiêu

Mục tiêu là sắp xếp nó chủ yếu theo People.Name.namevà thứ hai bởiPeople.Name.surname

Chướng ngại vật

Bây giờ, trong giải pháp cơ sở sử dụng ký hiệu khung để tính toán các thuộc tính để sắp xếp động. Tuy nhiên, ở đây, chúng ta sẽ phải xây dựng ký hiệu khung một cách linh hoạt, vì bạn sẽ mong đợi một số nhưPeople['Name.name'] sẽ hoạt động - điều này không có.

People['Name']['name']Mặt khác, chỉ đơn giản là làm tĩnh và chỉ cho phép bạn đi xuống n cấp thứ .

Giải pháp

Bổ sung chính ở đây sẽ là đi xuống cây đối tượng và xác định giá trị của lá cuối cùng, bạn phải chỉ định, cũng như bất kỳ lá trung gian nào.

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

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

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

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

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

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

Thí dụ

Ví dụ hoạt động trên JSBin


2
Tại sao? Đây không phải là câu trả lời cho câu hỏi ban đầu và "mục tiêu" có thể được giải quyết đơn giản với People.sort ((a, b) => {return a.Name.name.localeCompare (b.Name.name) || a.Name .surname.localeCompare (b.Name.surname)})
Tero Tolonen

12

Thêm một lựa chọn:

var someArray = [...];

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

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

sắp xếp tăng dần theo mặc định.


1
Phiên bản thay đổi một chút để sắp xếp theo nhiều trường có ở đây nếu cần: stackoverflow.com/questions/6913512/
Kẻ

có vẻ như nó có thể là cái tiếp theo: hàm export createdSortFn (prop: string, Reverse: boolean = false): (... args: any) => number {return (a, b) => {return a [prop ] <b [prop]? đảo ngược ? 1: -1: a [prop]> b [prop]? đảo ngược ? -1: 1: 0; }; }
Den Kerny

Tôi thích mã dễ đọc hơn mã ngắn nhất.
Rav Sơn Samandarov

đồng ý, nhưng trong một số trường hợp tôi không cần phải xem xét các chức năng tiện ích.
Den Kerny

11

Một hàm đơn giản sắp xếp một mảng đối tượng theo một thuộc tính

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

Sử dụng:

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

sortArray(objs, "last_nom"); // Asc
sortArray(objs, "last_nom", -1); // Desc

Giải pháp này hoạt động hoàn hảo cho tôi để sắp xếp hai chiều. Cảm ơn bạn
manjuvreddy

10

Một cách đơn giản:

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

Xem đó '.toLowerCase()'là cần thiết để ngăn chặn erros trong so sánh chuỗi.


4
Bạn có thể sử dụng các hàm mũi tên để cho mã thanh lịch hơn một chút:objs.sort( (a,b) => b.last_nom.toLowerCase() < a.last_nom.toLowerCase() );
Sertage

Điều này là sai vì lý do tương tự như được giải thích ở đây .
Patrick Roberts

2
Các chức năng mũi tên không xứng đáng với ES5. Hàng tấn động cơ vẫn bị giới hạn ở ES5. Trong trường hợp của tôi, tôi thấy câu trả lời ở trên tốt hơn đáng kể vì tôi đang sử dụng động cơ ES5 (do công ty của tôi ép buộc)
dylanh724

9

thông số desc bổ sung cho mã Ege canzcan

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

9

Kết hợp giải pháp năng động của Ege với ý tưởng của Vinay, bạn sẽ có được một giải pháp mạnh mẽ tuyệt vời:

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

Sử dụng:

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

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

//Unit Tests
arrObj.sortBy("LastName").print("LastName Ascending");
arrObj.sortBy("-LastName").print("LastName Descending");
arrObj.sortBy("LastName", "FirstName", "-Age").print("LastName Ascending, FirstName Ascending, Age Descending");
arrObj.sortBy("-FirstName", "Age").print("FirstName Descending, Age Ascending");
arrObj.sortBy("-Age").print("Age Descending");

1
Cảm ơn ý kiến! Nhân tiện, xin đừng khuyến khích mọi người thay đổi Nguyên mẫu mảng (xem cảnh báo ở cuối ví dụ của tôi).
Ege Özcan

8

Theo ví dụ của bạn, bạn cần sắp xếp theo hai trường (tên, họ), thay vì một. Bạn có thể sử dụng thư viện Alasql để thực hiện sắp xếp này trong một dòng:

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

Hãy thử ví dụ này tại jsFiddle .


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

1
Trên thực tế, nó dường như không hoạt động, đã phải sử dụng câu trả lời được chấp nhận. Nó không được sắp xếp chính xác.
madprops

8

Cho ví dụ ban đầu:

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

Sắp xếp theo nhiều lĩnh vực:

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

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>btương ứng.
  • ||trong dòng cuối cùng last_nomưu tiên hơn first_nom.
  • Phép trừ hoạt động trên các trường số: var age_order = left.age - right.age;
  • Phủ định để đảo ngược thứ tự, return -last_nom_order || -first_nom_order || -age_order;

8

Thử cái này,

UPTO ES5

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


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


IN ES6 & above:

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

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

giải pháp tốt nhất và dễ dàng
Omar Hasan

7

Bạn có thể cần phải chuyển đổi chúng sang chữ thường để tránh nhầm lẫn.

objs.sort(function (a,b) {

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

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

})

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

objs.sort(compare("last_nom"));

1
Vui lòng xem xét chỉnh sửa bài đăng của bạn để thêm giải thích về những gì mã của bạn làm và lý do tại sao nó sẽ giải quyết vấn đề. Một câu trả lời chủ yếu chỉ chứa mã (ngay cả khi nó hoạt động) thường không giúp OP hiểu vấn đề của họ.
Drenmi

7

Sử dụng Ramda,

npm cài đặt ramda

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

6

Đây là một vấn đề đơn giản, không biết tại sao mọi người có giải pháp phức tạp như vậy.
Một hàm sắp xếp đơn giản (dựa trên thuật toán sắp xếp nhanh ):

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

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

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

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

            return retVal;
        }

Sử dụng ví dụ:

var myArr = 
        [
            { val: 'x', idx: 3 },
            { val: 'y', idx: 2 },
            { val: 'z', idx: 5 },
        ];
myArr = sortObjectsArray(myArr, 'idx');

5
Làm thế nào là thực hiện sắp xếp nhanh chóng trong js một giải pháp đơn giản? Thuật toán đơn giản nhưng không phải là một giải pháp đơn giản.
Andrew

Nó đơn giản vì nó không sử dụng bất kỳ thư viện bên ngoài nào và nó không thay đổi nguyên mẫu của đối tượng. Theo tôi, độ dài của mã không có tác động trực tiếp đến độ phức tạp của mã
Gil Epshtain

2
Vâng, hãy để tôi thử với các từ khác nhau: Làm thế nào để phát minh lại bánh xe là một giải pháp đơn giản?
Roberto14
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.