Сортировать массив объектов по значению свойства строки


2765

У меня есть массив объектов 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

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

Ответы:


3923

Достаточно просто написать собственную функцию сравнения:

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

Или встроенный (с Марко Демайо):

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

441
Или встроенный: objs.sort (function (a, b) {return (a.last_nom> b.last_nom)? 1: ((b.last_nom> a.last_nom)? -1: 0);});
Марко Демайо


177
return a.last_nom.localeCompare(b.last_nom)тоже будет работать
Цербр

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 могут быть любой строкой, и если у вас есть свойства, начинающиеся с «-» (крайне маловероятно и, вероятно, не очень хорошая идея), вам нужно изменить функцию dynamicSort, чтобы использовать что-то другое в качестве обратной сортировки показатель.
Эге Озкан

Я заметил, что 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
dzh

1
Это отличное решение, но есть одна проблема, если сравнивать числа. Пожалуйста, добавьте это перед проверкой:if( !isNaN(a[property]) ) a[property] = Number(a[property]); if( !isNaN(b[property]) ) b[property] = Number(b[property]);
Ivijan Stefan Stipić

417

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

1
Чтобы сделать это по убыванию чисел: .sort ((a, b) => b.numberProperty - a.numberProperty). По возрастанию: .sort ((a, b) => a.numberProperty - b.numberProperty)
Себастьян Паттен

188

underscore.js

используйте подчеркивание, его маленький и удивительный ...

sortBy_.sortBy (list, iterator, [context]) Возвращает отсортированную копию списка, ранжированного в порядке возрастания по результатам выполнения каждого значения через итератор. Итератор также может быть строковым именем свойства для сортировки (например, длина).

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
вам нужно загрузить библиотеку javascript "underscore":<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'] );
Трэвис Хитер

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, но вышеприведенная функция возвращает логическое значение. Сортировка отлично работает в Chrome, но не работает, например, в PhantomJS. См. Code.google.com/p/phantomjs/issues/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;
});

@BadFeelingAboutЭто что означает возврат -1 или 1? Я понимаю, что -1 буквально означает, что A меньше, чем B только по синтаксису, но зачем использовать 1 или -1? Я вижу, что все используют эти числа в качестве возвращаемых значений, но почему? Спасибо.
Chris22

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

@BadFeelingAboutЭто спасибо за объяснение и ссылку. Хотите верьте, хотите нет, но я погуглил различные фрагменты кода, используя 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
Это не просто вернуть другой массив. но на самом деле сортирует оригинал!
Винай Аггарвал

Если вы хотите убедиться, что вы используете естественную сортировку с числами (то есть 0,1,2,10,11 и т. Д.), Используйте parseInt с набором Radix. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… so: 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
никогда, никогда не предлагайте решение, которое модифицирует прототип основного объекта
pbanka

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Используется для сравнения атрибутов объекта ( цифры в данном случае) может быть принято для различных времен данных. Например, регулярное выражение может использоваться для сравнения каждой пары соседних строк .
ноября

1
@ 0leg Я бы хотел увидеть здесь пример использования регулярных выражений для сравнения строк таким образом.
Боб Стейн

Эта реализация довольно хороша, если вам нужно отсортировать ее по ID. Да, вы предложили использовать регулярное выражение для сравнения соседней строки, что усложняет решение, тогда как цель этой упрощенной версии будет иной, если регулярное выражение используется вместе с данным решением. Простота - лучшее.
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
@ Жан-Франсуа Бошам, ваше решение работает отлично и намного лучше.
Ahsan

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 чтобы указать путь к свойству, которое должно соответствовать. Мы отсортируем пользовательские объекты по вложенному свойству age. Да, это позволяет сопоставлять вложенные свойства!

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

Хотите, чтобы это было наоборот? Нет проблем. Используйте _.reverse .

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

Ты можешь использовать

Самый простой способ: Lodash

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

Этот метод похож на _.sortBy, за исключением того, что он позволяет указывать порядок сортировки итерируемых для сортировки. Если заказы не указаны, все значения сортируются в порядке возрастания. В противном случае укажите порядок «desc» для убывающего или «asc» для возрастающего порядка сортировки соответствующих значений.

аргументы

collection (Array | Object): коллекция для повторения. [iteratees = [_. identity]] (Array [] | Function [] | Object [] | string []): Итерации для сортировки. [orders] (string []): порядок сортировки итераций.

Возвращает

(Массив): возвращает новый отсортированный массив.


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

Здесь есть много хороших ответов, но я хотел бы отметить, что они могут быть расширены очень просто для достижения гораздо более сложной сортировки. Единственное, что вам нужно сделать, это использовать оператор OR для цепочки функций сравнения:

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

Где fn1, fn2... - функции сортировки, которые возвращают [-1,0,1]. Это приводит к "сортировке по fn1", "сортировке по fn2", которая почти равна ORDER BY в SQL.

Это решение основано на поведении ||оператора, который оценивает первое вычисленное выражение, которое может быть преобразовано в true .

Самая простая форма имеет только одну встроенную функцию, например:

// 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 и т. Д.). Поэтому, если «а» находится в индексе 5 и вы возвращаете -1, то 5 + -1 == 4 (переместите его ближе к фронту) 5 + 0 == 5 (держать его там, где он есть) и т. д. Он обходит массив, сравнивая 2 соседей каждый раз, пока не достигает конца, оставляя отсортированный массив.
Джейми Мейсон

Спасибо, что нашли время, чтобы объяснить это дальше. Поэтому, используя ваше объяснение и MDN Array.prototype.sort , я расскажу вам, что я об этом думаю: по сравнению с aи b, если aон больше, чем b1, добавьте к индексу 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()принимает значение, 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 .

проблема

Я столкнулся с ниже и не мог изменить это. Я также не хотел временно сплющивать объект. Также я не хотел использовать подчеркивание / lodash, в основном из соображений производительности и удовольствия, чтобы реализовать это самостоятельно.

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'], с другой стороны, является статичным и позволяет вам только спуститься на n-й уровень.

Решение

Основным дополнением здесь будет прогулка по дереву объектов и определение значения последнего листа, который вы должны указать, а также любого промежуточного листа.

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)})
Теро Толонен

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/…
Равшан Самандаров

похоже, что это может быть следующим: функция экспорта generateSortFn (prop: string, reverse: boolean = false): (... args: any) => number {return (a, b) => {return a [prop ] <b [prop]? обеспечить регресс ? 1: -1: a [prop]> b [prop]? обеспечить регресс ? -1: 1: 0; }; }
Ден Керни

Я бы предпочел более читаемый код, чем самый короткий.
Равшан Самандаров

согласился, но в некоторых случаях у меня нет необходимости смотреть на служебные функции.
Ден Керни

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

Это решение отлично сработало для меня, чтобы сортировать в двух направлениях. Спасибо и
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() );
Sertage

Это неправильно по той же причине, что и здесь .
Патрик Робертс

2
Функции стрелок не соответствуют ES5. Тонны двигателей по-прежнему ограничены ES5. В моем случае я нахожу ответ выше значительно лучше, так как я нахожусь на двигателе ES5 (принудительно моей компанией)
dylanh724

9

дополнительные параметры desc для кода Ege Özcan

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

Используя Рамду,

нпм установить рамду

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
Что ж, позвольте мне попробовать с другими словами: как изобретать колесо является простым решением?
Roberto14
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.