Сортувати масив об'єктів за значенням властивості рядка


2763

У мене є масив об’єктів JavaScript:

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

Як можна їх сортувати за значенням last_nom JavaScript?

Я знаю про це sort(a,b), але це, здається, працює лише на рядках і числах. Чи потрібно додати toString()метод до своїх об’єктів?


7
Цей сценарій дозволяє робити саме це, якщо ви не хочете написати власну функцію порівняння або сортувальник: thomasfrank.se/sorting_things.html
Baversjo

найшвидший спосіб полягає у використанні ізоморфного модуля сортування масиву, який працює в обох веб-переглядачах і на вузлі, підтримуючи будь-який тип введення, обчислювані поля та замовлення на замовлення.
Ллойд

Відповіді:


3918

Написати власну функцію порівняння досить просто:

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

Або вбудований (c / o Marco Demaio):

objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0)); 

441
Або вбудований: objs.sort (функція (a, b) {return (a.last_nom> b.last_nom)? 1: ((b.last_nom> a.last_nom)? -1: 0);});
Марко Демайо


176
return a.last_nom.localeCompare(b.last_nom)буде теж працювати.
Cerbrus

146
для тих, хто шукає сорт, де поле числове, тіло функції порівняння: return a.value - b.value;(ASC)
Andre Figueiredo

20
@Cerbrus localeCompareважливий при використанні наголошених символів на іноземних мовах, а також більш елегантним.
Маркос Ліма

839

Ви також можете створити функцію динамічного сортування, яка сортує об’єкти за значенням, яке ви передаєте:

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

Таким чином, ви можете мати масив таких об’єктів:

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

... і це спрацює, коли ви зробите:

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

Насправді це вже відповідає на питання. Внизу написано, тому що багато людей звернулися до мене, скаржачись, що він не працює з кількома параметрами .

Кілька параметрів

Ви можете використовувати функцію нижче для створення функцій сортування з кількома параметрами сортування.

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

Що б дозволило вам зробити щось подібне:

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

Масив підкласифікації

Для щасливчиків серед нас, хто може використовувати ES6, що дозволяє розширювати рідні об’єкти:

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

Це дозволило б це:

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

Зауважте, що імена властивостей у JavaScript можуть бути будь-якими рядками, і якщо у вас є властивості, починаючи з "-" (вкрай малоймовірно і, мабуть, не є гарною ідеєю), вам потрібно буде змінити функцію dinamiSort, щоб використовувати щось інше в якості зворотного сортування показник.
Еге Оскан

Я помітив, що dynamicSort()у наведеному вище прикладі великі літери випереджають малі літери. Наприклад, якщо у мене є значення APd, Aklin, і Abe- результати в ASC - то повинно бути Abe, Aklin, APd. Але з вашим прикладом, результати APd, Abe, Aklin. Все-таки виправити таку поведінку?
Ллойд Бенкс

6
@LloydBanks, якщо ви використовуєте це строго для рядків, ви можете використовувати var result = a[property].localeCompare(b[property]);замість var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;.
Еге Оскан

1
@ EgeÖzcan Він повинен або помістити елементи у верхній або нижній частині , ось реалізація я закінчив з використанням pastebin.com/g4dt23jU
Дж

1
Це чудове рішення, але у вас є одна проблема, якщо порівнювати цифри. Будь ласка, додайте це перед перевіркою:if( !isNaN(a[property]) ) a[property] = Number(a[property]); if( !isNaN(b[property]) ) b[property] = Number(b[property]);
Ivijan Stefan Stipić

414

В ES6 / ES2015 або новіших версіях ви можете це зробити:

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

До ES6 / ES2015

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

29
це доступно з JS 1.1, шматок жирної стрілки до цього - шматок ES6 / 2015. Але все-таки дуже корисна і найкраща відповідь на мій погляд
Джон Хардінг

13
@PratikKelwalkar: якщо вам потрібно повернути його, просто переключіть a і b порівняння: objs.sort ((a, b) => b.last_nom.localeCompare (a.last_nom));
Влад Безден

чи можна використовувати індекс також для адреси поля для сортування: замість того, щоб last_nomвикористовувати просто число в масиві 1:?
і-бри

4
@VladBezden дякую за вашу відповідь! Це рішення є першим з дуже невеликими програмними зусиллями та правильними результатами сортування та масивом рядків на зразок: ["Name1", "Name10", "Name2", "щось ще", "Name11"]. Мені вдалося правильно сортувати роботуobjs.sort((a, b) => a.last_nom.localeCompare(b.last_nom, undefined, {numberic: true}));
науковець

1
Для цього з низхідними числами: .sort ((a, b) => b.numberProperty - a.numberProperty). Вихід: .sort ((a, b) => a.numberProperty - b.numberProperty)
Себастьян Паттен

188

underscore.js

використовувати підкреслення, його маленькі та дивовижні ...

sortBy_.sortBy (список, ітератор, [контекст]) Повертає відсортовану копію списку, класифіковану у порядку зростання за результатами запуску кожного значення через ітератор. Ітератор також може бути назвою рядка властивості для сортування (наприклад, довжина).

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
Девід, могли б ви змінити відповідь сказати, var sortedObjs = _.sortBy( objs, 'first_nom' );. objsбуде НЕ сортувати себе в результаті цього. Функція поверне відсортований масив. Це зробило б це більш явним.
Джесс

10
Для зворотного сортування:var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse();
Ердаль Г.

2
вам потрібно завантажити jab libary "підкреслення":<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"> </script>
and-bri

5
Також доступний Lodashдля тих, хто віддає перевагу цьому
WoJ

2
У lodash це було б те саме: var sortedObjs = _.sortBy( objs, 'first_nom' );або якщо ви хочете в іншому порядку:var sortedObjs = _.orderBy( objs, ['first_nom'],['dsc'] );
Travis Heeter

186

Не розумійте, чому люди роблять це так складно:

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

Для більш жорстких двигунів:

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

Поміняйте оператор, щоб він був сортований за зворотним алфавітом.


17
Це насправді неправильно, оскільки функція, що використовується у сортуванні, повинна повертати -1, 0 або 1, але наведена вище функція повертає булеву форму. Сорт прекрасно працює в хромі, але не вдається, наприклад, у PhantomJS. Дивіться code.google.com/p/phantomjs/isissue/detail?id=1090
schup

Деякі двигуни пояснюють дурість, таким чином це було якось експлуатувати. Я оновив свою відповідь належною версією.
p3lim

19
Я б запропонував редагувати, щоб вивести першу версію. Його більш лаконічне виглядає привабливіше, але це не працює, принаймні не надійно. Якщо хтось спробує це в одному браузері, і він працює, він може навіть не усвідомлювати, що у них є проблеми (особливо якщо вони не прочитали коментарі). Друга версія працює належним чином, тому в першій справді немає потреби.
Кейт

2
@Simon Я дуже вдячний за те, що спочатку "трохи неправильну" версію, оскільки більш сувора реалізація займає декілька секунд для розбору та розуміння, і без цього було б набагато важче.
Аарон Шерман

1
@ Lion789 просто так:if(a.count == b.count) return a.name > b.name; else return a.count > b.count;
p3lim

62

Якщо у вас є дублюючі прізвища, ви можете сортувати їх за іменем,

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 AboutThis, що означає повернення -1 або 1? Я розумію, що -1 буквально означає, що A менше за B синтаксисом, але навіщо використовувати 1 або -1? Я бачу, що всі використовують ці числа як зворотні значення, але чому? Дякую.
Chris22

1
@ Chris22 негативне число, що повертається, означає, що bмає з’явитися після aмасиву. Якщо позитивне число повернеться, значить, воно aповинно з’явитися після b. Якщо 0повернуто, це означає, що вони вважаються рівними. Ви завжди можете прочитати документацію: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
BadFeeling AboutThis

@BadFeeling AboutЦе спасибі, за пояснення та посилання. Вірите чи ні, я гугл різними фрагментами коду, використовуючи 1, 0, -1раніше, ніж я запитав це тут. Я просто не знайшов потрібної мені інформації.
Chris22

48

Просте і швидке вирішення цієї проблеми за допомогою успадкування прототипу:

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

Приклад / використання

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

Оновлення: Більше не змінюється оригінальний масив.


5
Це не просто повертає інший масив. але насправді сортує оригінальний !.
Vinay Aggarwal

Якщо ви хочете переконатися, що ви використовуєте природний сорт із числами (тобто 0,1,2,10,11 тощо), використовуйте parseInt з набором Radix. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… так: return (parseInt (a [p], 10)> parseInt (b [p], 10))? 1: (parseInt (a [p], 10) <parseInt (b [p], 10))? -1: 0;
Пол

@codehuntr Дякуємо, що виправили це. але я думаю, замість того, щоб робити функцію сортування, щоб зробити цю сенсибілізацію, краще, якщо ми зробимо окрему функцію для фіксації типів даних. Оскільки функція сортування не може визначити, яке властивість буде містити тип даних. :)
Vinay Aggarwal

Дуже хороший. Переверніть стрілки для отримання ефекту asc / desc.
Абдул Садік Ялцін

4
ніколи, ніколи не
пропонуйте

39

Станом на 2018 рік є набагато коротше та елегантніше рішення. Просто використовуйте. Array.prototype.sort () .

Приклад:

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
У питаннях рядки використовувались для порівняння на відміну від чисел. Ваша відповідь чудово підходить для сортування за номерами, але не настільки хороша для порівняння за рядками.
smcstewart

a.value - b.valueВикористовується для порівняння атрибутів об'єкта ( цифри в даному випадку) може бути прийнято для різних часів даних. Наприклад, для порівняння кожної пари сусідніх рядків можна використовувати регулярний вираз .
0лег

1
@ 0leg Я хотів би побачити тут приклад використання регексу для порівняння рядків таким чином.
Боб Штейн

Ця реалізація є досить хорошою, якщо вам потрібно сортувати її за ідентифікатором. Так, ви запропонували використовувати регулярний вимір для порівняння сусіднього рядка, що ускладнює рішення, тоді як мета цієї спрощеної версії буде інакше, якщо регекс використовуватиметься із заданим рішенням. Простота - найкраща.
surendrapanday

33

Стара відповідь, яка не є правильною:

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

ОНОВЛЕННЯ

З коментаря Бошампа:

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

Більш читаний формат:

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

Без вкладених територій:

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

Пояснення: Number()буде передано trueв 1і falseдо 0.


Це працює, але результат чомусь нестабільний
TitanFighter

@ AO17 ні, це не буде. Ви не можете відняти рядки.
Патрік Робертс

15
Це слід зробити так:arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))
Жан-Франсуа Бошам

3
@ Jean-FrançoisBeauchamp, ваше рішення працює прекрасно і набагато краще.
Ахсан

3-й з номером - це просто і приємно!
Коджо

29

Замість використання спеціальної функції порівняння ви також можете створити тип об'єкта за допомогою спеціального toString()методу (який викликається функцією порівняння за замовчуванням):

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 ( суперсеть Underscore.js )

Добре не додавати рамки для кожної простої логіки, але покладаючись на добре перевірені рамки утиліти, можна прискорити розвиток і зменшити кількість помилок.

Lodash виробляє дуже чистий код і просуває більш функціональне програмування стиль . З одного погляду стає зрозуміло, у чому полягає ціль коду.

Питання ОП можна просто вирішити як:

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

Більше інформації? Наприклад, у нас є наступний вкладений об'єкт:

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

Тепер ми можемо використовувати скорочення _.property,user.age щоб вказати шлях до властивості, який повинен відповідати. Ми будемо сортувати об’єкти користувача за властивістю вкладеного віку. Так, це дозволяє встановити відповідність вкладених властивостей!

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

Хочете, щоб це було відмінено? Нема проблем. Використовуйте _.реверс .

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

Хочете поєднати обидва за допомогою ланцюжка ?

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

Або коли ви віддаєте перевагу перетікання по ланцюгу

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

28

Можна використовувати

Найпростіший спосіб: Лодаш

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

Цей метод схожий на _.sortBy, за винятком того, що він дозволяє вказувати порядки сортування ітераторів для сортування. Якщо замовлення не визначені, усі значення сортуються у порядку зростання. В іншому випадку вкажіть порядок "desc" для спадання або "asc" для порядку порядку сортування відповідних значень.

Аргументи

collection (масив | об'єкт): колекція повторюється. [iteratees = [_. ідентифікація]] (масив [] | функція [] | об'єкт [] | рядок []): ітерації для сортування за. [порядки] (рядок []): порядки сортування ітераторів.

Повертається

(Масив): Повертає новий відсортований масив.


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

Тут є багато хороших відповідей, але я хотів би зазначити, що їх можна розширити дуже просто, щоб досягти набагато складнішого сортування. Єдине, що вам потрібно зробити, це використовувати оператор АБО для ланцюга таких функцій порівняння:

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

Де fn1,fn2 ... є функціями сортування , які повертають [-1,0,1]. Це призводить до "сортування за fn1", "сортування за fn2", що майже дорівнює ORDER BY у SQL.

Це рішення засноване на поведінці ||оператора, який оцінює перший оцінений вираз, який може бути перетворений в істинний .

Найпростіша форма має лише одну вбудовану функцію, як ця:

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

Виконуючи два кроки last_nom, first_nomпорядок сортування виглядатиме так:

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

Загальна функція порівняння може бути приблизно такою:

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

Цю функцію можна розширити на підтримку числових полів, чутливості регістру, довільних типів даних тощо.

Ви можете використовувати їх із ланцюжком, упорядковуючи їх:

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

Сенс у тому, що чистий JavaScript з функціональним підходом може пройти довгий шлях без зовнішніх бібліотек або складного коду. Він також дуже ефективний, оскільки не потрібно робити розбір рядків


23

Приклад використання:

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

Сценарій:

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

дякую, що ви зламали це, я намагаюся зрозуміти, чому цифри 1, 0, -1використовуються для впорядкування сортування. Навіть з вашим поясненням вище, яке виглядає дуже добре - я все ще не дуже розумію це. Я завжди думаю про те -1, коли використовується властивість довжини масиву, тобто: arr.length = -1означає, що елемент не знайдено. Я, мабуть, тут змішую речі, але ви можете допомогти мені зрозуміти, чому цифри 1, 0, -1використовуються для визначення порядку? Дякую.
Chris22

1
Це не зовсім точно, але це може допомогти подумати про це так: функція, передана array.sort, викликається один раз для кожного елемента масиву, як аргумент з назвою "a". Зворотне значення кожного виклику функції полягає в тому, як слід змінити індекс (номер поточної позиції) елемента "a" порівняно з наступним пунктом "b". Індекс диктує порядок масиву (0, 1, 2 тощо). Отже, якщо "a" знаходиться в індексі 5, і ви повернете -1, то 5 + -1 == 4 (перемістіть його ближче спереду) 5 + 0 == 5 (тримати його там, де він є) і т. д. Він проходить масив, порівнюючи 2 сусідки кожен раз, поки не досягне кінця, залишаючи відсортований масив.
Джеймі Мейсон

дякую, що знайшли час, щоб пояснити це далі. Отож, використовуючи ваше пояснення та MDN Array.prototype.sort , я скажу вам, що я про це думаю: порівняно з aта b, якщо aбільше, ніж bдодати 1 до індексу aта розмістити його позаду b, якщо aменше, ніж b, відніміть 1 aі поставте його перед b. Якщо aі bоднакові, додайте 0 aі залиште його там, де він є.
Chris22

22

Я не бачив цей конкретний підхід , запропонований, так ось метод порівняння лаконічна мені подобається використовувати , яка працює для обох stringі number:

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

Ось пояснення sortBy() :

sortBy()приймає a, fnщо вибирає значення об'єкта для використання для порівняння та повертає функцію, яку можна передавати безпосередньо Array.prototype.sort(). У цьому прикладі ми використовуємо o.last_nomяк значення для порівняння, тому кожен раз, коли ми отримуємо два об’єкти через Array.prototype.sort()такі

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

і

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

ми використовуємо

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

щоб порівняти їх.

Пам'ятаючи це fn = o => o.last_nom, ми можемо розширити функцію порівняння до еквівалента

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

Логічний ||оператор АБО має функціонал короткого замикання, який тут дуже корисний. Через те, як це працює, тіло функції, зазначеної вище, означає

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

Як додатковий бонус, ось еквівалент у ECMAScript 5 без функцій зі стрілками, який, на жаль, є більш багатослівним:

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

Я знаю, що це питання занадто старе, але я не бачив жодної реалізації, подібної до моєї.
Ця версія заснована на ідіомі трансформації Шварца .

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

Ось приклад, як його використовувати:

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

Сортування (детальніше) Складні масиви об'єктів

Оскільки ви, мабуть, стикаєтеся зі складнішими структурами даних, такими як цей масив, я би розширив рішення.

TL; DR

Більш підключена версія заснована на дуже чудовій відповіді @ ege-Özcan .

Проблема

Я зіткнувся із наведеним нижче і не зміг його змінити. Я також не хотів тимчасово розгладжувати об’єкт. Я також не хотів використовувати підкреслення / лодаш, головним чином, з міркувань продуктивності та задоволення від того, щоб його реалізувати сам.

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

Мета

Мета полягає в тому, щоб сортувати її в першу чергу за People.Name.nameдругоюPeople.Name.surname

Перешкоди

Тепер у базовому рішенні використовується позначення дужок для обчислення властивостей для сортування за динамічним режимом. Тут, однак, нам також доведеться динамічно конструювати позначення дужок, оскільки ви очікуєте подібногоPeople['Name.name'] , спрацює що не працює.

Просто робити People['Name']['name'], з іншого боку, є статичним і дозволяє лише спускатися вниз по н -го рівня.

Рішення

Основним доповненням тут буде спуск по дереву об'єктів та визначення значення останнього аркуша, який потрібно вказати, як і будь-який лист посередника.

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

Приклад

Робочий приклад щодо JSBin


2
Чому? Це не відповідь на початкове запитання, і "мета" може бути вирішена просто за допомогою People.sort ((a, b) => {return a.Name.name.localeCompare (b.Name.name) || a.Name .surname.localeCompare (b.Name.surname)})
Tero Tolonen

12

Ще один варіант:

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

сортує за зростанням за замовчуванням.


1
Трохи змінився версія для сортування по декількох полях тут , якщо це необхідно: stackoverflow.com/questions/6913512 / ...
Равшан Самандар

схоже, що це може бути наступне: функція експорту createSortFn (prop: string, reverse: boolean = false): (... args: any) => число {return (a, b) => {return a [prop ] <b [опора]? зворотний ? 1: -1: a [опора]> b [опора]? зворотний ? -1: 1: 0; }; }
Den Kerny

Я вважаю за краще читати код, ніж найкоротший.
Равшан Самандаров

погодились, але в деяких випадках у мене немає потреби дивитися на функції утиліти.
Ден Керні

11

Проста функція, яка сортує масив об’єкта за властивістю

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
}

Використання:

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

Це рішення прекрасно працювало для мене для сортування двонаправлених. Дякую u
manjuvreddy

10

Простий спосіб:

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

Дивіться, що '.toLowerCase()'необхідно для запобігання помилок у порівнянні рядків.


4
Ви можете використовувати функції стрілок, щоб дозволити коду трохи елегантніше:objs.sort( (a,b) => b.last_nom.toLowerCase() < a.last_nom.toLowerCase() );
Сердж

Це неправильно з тієї ж причини, що пояснюється тут .
Патрік Робертс

2
Функції стрілки не гідні ES5. Тони двигунів все ще обмежені ES5. У моєму випадку я вважаю відповідь вище значно кращою, оскільки я працюю на двигуні ES5 (змушений моєю компанією)
dylanh724

9

додаткові параметри опису для коду Еге Оскана

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

Поєднуючи динамічне рішення Ege з ідеєю Vinay, ви отримуєте приємне надійне рішення:

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

Використання:

// 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
Дякую за ідею! До речі, будь ласка, не спонукайте людей змінювати прототип масиву (див. Попередження в кінці мого прикладу).
Еге Оскан

8

Записуючи свій приклад, потрібно сортувати за двома полями (прізвище, ім’я), а не за одним. Ви можете використовувати бібліотеку Alasql, щоб зробити такий сорт в одному рядку:

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

Спробуйте цей приклад на jsFiddle .


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

1
Насправді це, здавалося, не спрацювало, довелося використовувати прийняту відповідь. Це було неправильне сортування.
madprops

8

З огляду на оригінальний приклад:

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

Сортувати за кількома полями:

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

Примітки

  • a.localeCompare(b)є загальноприйнятим і повертає -1,0,1 якщо a<b, a==b,a>b відповідно.
  • ||в останньому рядку надається last_nomпріоритет надfirst_nom .
  • Віднімання працює на числових полях: var age_order = left.age - right.age;
  • Відмовитись у зворотному порядку, return -last_nom_order || -first_nom_order || -age_order;

8

Спробуйте це,

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

найкраще і просте рішення
Омар Хасан

7

Можливо, вам доведеться перетворити їх на малі регістри, щоб уникнути плутанини.

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
Будь ласка, подумайте про редагування своєї публікації, щоб додати більше пояснень про те, що робить ваш код і чому це вирішить проблему. Відповідь, що здебільшого просто містить код (навіть якщо він працює), зазвичай не допомагає ОП зрозуміти свою проблему.
Дренмі

7

Використовуючи Рамду,

npm встановити 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

Це проста проблема, не знаю, чому люди мають таке складне рішення.
Проста функція сортування (заснована на алгоритмі швидкого сортування ):

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

Приклад використання:

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

5
Як реалізувати швидке сортування у js просте рішення? Простий алгоритм, але не просте рішення.
Андрій

Це просто, оскільки він не використовує жодних зовнішніх бібліотек і не змінює прототип об'єкта. На мою думку, довжина коду не має прямого впливу на складність коду
Гіл Епштейн

2
Ну, дозвольте спробувати з різними словами: Як винаходити колесо - це просте рішення?
Роберто14
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.