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

2045

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

  • 3
    Этот скрипт позволяет вам делать это, если вы не хотите написать свою собственную функцию сравнения или сортировщик: thomasfrank.se/sorting_things.html
Теги:
arrays
sorting
properties

39 ответов

2732
Лучший ответ

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

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

Или inline (c/o Marco Demaio):

objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0)); 
  • 370
    Или встроенный: objs.sort (function (a, b) {return (a.last_nom> b.last_nom)? 1: ((b.last_nom> a.last_nom)? -1: 0);});
  • 0
    Большое спасибо, но я изо всех сил пытаюсь увидеть логику, которой следует функция ... что такое a & b? они 1-й и 2-й, затем 2-й, 3-й, затем 3-й, 4-й?!?
Показать ещё 22 комментария
707

Вы также можете создать динамическую функцию сортировки, которая сортирует объекты по их значению, которое вы передаете:

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

Итак, у вас может быть массив таких объектов:

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

Добавление его в прототип

(Реализация, которая находится чуть ниже, вдохновлена ​​Mike R ответить)

Я бы не рекомендовал менять прототип собственного объекта, а просто привести пример, чтобы вы могли реализовать его на своих собственных объектах. Для сред, поддерживающих его, вы также можете использовать Object.defineProperty, как показано в следующем разделе, который, по крайней мере, не имеет отрицательного побочного эффекта перечислимого, как описано в последней части)

Реализация прототипа будет выглядеть примерно следующим образом (Вот рабочий пример):

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

"OK" способ добавления его в прототип

Если вы нацеливаете IE v9.0 и выше, то, как я уже упоминал ранее, используйте Object.defineProperty (рабочий пример):

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

Это может быть приемлемым компромиссом до тех пор, пока не появится оператор привязки.

Все эти прототипы позволяют:

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

Вы должны прочитать это

Если вы используете метод прямого прототипа доступа (Object.defineProperty в порядке), а другой код не проверяет hasOwnProperty, котята умирают! Хорошо, честно говоря, никакого вреда для котенка не наносит никакого вреда, но, вероятно, что-то сломается, и каждый другой разработчик в вашей команде будет вас ненавидеть:

Изображение 2068

Посмотрите, что последний "SortBy"? Да. Не круто. Используйте Object.defineProperty, где вы можете, и оставите только Array.prototype в противном случае.

  • 0
    Обратите внимание, что имена свойств в JavaScript могут быть любой строкой, и если у вас есть свойства, начинающиеся с «-» (крайне маловероятно и, вероятно, не очень хорошая идея), вам нужно изменить функцию dynamicSort, чтобы использовать что-то другое в качестве обратной сортировки индикатор.
  • 0
    Я заметил, что dynamicSort() в приведенном выше примере поместит заглавные буквы перед строчными. Например, если у меня есть значения APd , Aklin и Abe - результаты в ASC - то должно быть Abe , Aklin , APd . Но с вашим примером, результаты - APd , Abe , Aklin . В любом случае, чтобы исправить это поведение?
Показать ещё 7 комментариев
166

В ES6/ES2015 или более поздней версии вы можете сделать так:

objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom));
  • 18
    это было доступно начиная с JS 1.1, жирная стрелка для этого - ES6 / 2015. Но все же очень полезно, и лучший ответ на мой взгляд
  • 9
    @PratikKelwalkar: если вам нужно повернуть вспять, просто переключите сравнение a и b: objs.sort ((a, b) => b.last_nom.localeCompare (a.last_nom));
Показать ещё 2 комментария
162

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' );
  • 17
    Дэвид, не могли бы вы отредактировать ответ, чтобы сказать: var sortedObjs = _.sortBy( objs, 'first_nom' ); , objs не будут отсортированы в результате этого. Функция вернет отсортированный массив. Это сделало бы это более явным.
  • 8
    Для обратной сортировки: var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse();
Показать ещё 3 комментария
146

Не поймите, почему люди делают это настолько сложным:

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

Переведите оператор, чтобы он отсортировался по обратному алфавитному порядку.

  • 13
    Это на самом деле не правильно, так как функция, используемая в сортировке, должна возвращать -1, 0 или 1, но вышеприведенная функция возвращает логическое значение. Сортировка отлично работает в Chrome, но не работает, например, в PhantomJS. См. Code.google.com/p/phantomjs/issues/detail?id=1090.
  • 0
    Некоторые двигатели объясняют глупость, этот способ был своего рода эксплуатацией. Я обновил свой ответ правильной версией.
Показать ещё 5 комментариев
52

Если у вас есть повторяющиеся фамилии, вы можете отсортировать их по имени -

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;
});
  • 37
    Что форматирование a< b a >b интересно.
  • 0
    @BadFeelingAboutЭто что означает возврат -1 или 1? Я понимаю, что -1 буквально означает, что A меньше, чем B только по синтаксису, но зачем использовать 1 или -1? Я вижу, что все используют эти числа в качестве возвращаемых значений, но почему? Благодарю.
Показать ещё 2 комментария
37

Простое и быстрое решение этой проблемы с использованием наследования прототипов:

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
    Если вы хотите убедиться, что вы используете естественную сортировку с числами (то есть 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;
Показать ещё 3 комментария
24

Вместо использования пользовательской функции сравнения вы также можете создать тип объекта с помощью настраиваемого метода 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();
15

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

Хотите объединить оба с помощью Chaining?

const sortedObjs = _.chain(users).sortBy('user.age').reverse().value();
15

Здесь есть много хороших ответов, но я хотел бы указать, что они могут быть расширены очень просто, чтобы добиться более сложной сортировки. Единственное, что вам нужно сделать, это использовать оператор 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 с функциональным подходом может пройти долгий путь без внешних библиотек или сложного кода. Это также очень эффективно, так как не нужно проводить синтаксический анализ строк

14

Я знаю, что этот вопрос слишком стар, но я не видел никакой реализации, подобной моей.
Эта версия основана на идее преобразования Шварца.

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: 'Pako',              rating: 4.21 },
  { name: 'Hill Climb Racing', rating: 3.88 },
  { name: 'Angry Birds Space', rating: 3.88 },
  { name: 'Badland',           rating: 4.33 }
];

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

Пример использования:

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

Script:

/**
 * @description 
 * Returns a function which will sort an
 * array of objects by the given key.
 * 
 * @param  {String}  key
 * @param  {Boolean} reverse
 * @return {Function}     
 */
function 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.
  var 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.
  var moveLarger = reverse ? -1 : 1;

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

}
  • 0
    спасибо, что разбили это, я пытаюсь понять, почему цифры 1, 0, -1 используются для сортировки. Даже с вашим объяснением выше, которое выглядит очень хорошо - я все еще не совсем понимаю. Я всегда думаю о -1 как при использовании свойства длины массива, т.е.: arr.length = -1 означает, что элемент не найден. Я, наверное, здесь все перемешиваю, но не могли бы вы помочь мне понять, почему цифры 1, 0, -1 используются для определения порядка? Благодарю.
  • 1
    Это не совсем точно, но это может помочь думать об этом так: функция, переданная в array.sort, вызывается один раз для каждого элемента в массиве в качестве аргумента с именем «a». Возвращаемое значение каждого вызова функции - это то, как индекс (номер текущей позиции) элемента «a» должен быть изменен по сравнению со следующим элементом «b». Индекс диктует порядок массива (0, 1, 2 и т. Д.). Поэтому, если «а» находится в индексе 5, а вы возвращаете -1, то 5 + -1 == 4 (переместите его ближе к фронту) 5 + 0 == 5 (держать его там, где он есть) и т. д. Он обходит массив, сравнивая 2 соседей каждый раз, пока не достигает конца, оставляя отсортированный массив.
Показать ещё 2 комментария
13

У меня есть часть кода, которая работает для меня:

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

UPDATE: не работает всегда, поэтому это неверно: (

  • 0
    Это работает, но результат по какой-то причине нестабилен
  • 0
    используйте «-» вместо «>». Это будет работать.
Показать ещё 3 комментария
12

Сортировка (подробнее) Комплексные массивы объектов

Поскольку вы, вероятно, сталкиваетесь с более сложными структурами данных, такими как этот массив, я бы расширил решение.

TL; DR

Более плавная версия на основе @ege-Özcan очень приятная answer.

Проблема

Я столкнулся с ниже и не мог его изменить. Я также не хотел временно сгладить объект. Я также не хотел использовать underscore/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`- 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)})
11

С 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;
});
  • 5
    В вопросе строки были использованы для сравнения, а не цифры. Ваш ответ отлично подходит для сортировки по числам, но не так хорош для сравнения по строке.
  • 0
    Значение a.value - b.value используемое для сравнения атрибутов объекта (в данном случае чисел ), может быть принято для различных времен данных. Например, регулярное выражение может использоваться для сравнения каждой пары соседних строк .
Показать ещё 1 комментарий
9

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

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

Смотрите, что '.toLowerCase()' необходимо для предотвращения erros при сравнении строк.

  • 4
    Вы можете использовать функции стрелок, чтобы сделать код немного более элегантным: objs.sort( (a,b) => b.last_nom.toLowerCase() < a.last_nom.toLowerCase() );
  • 0
    Это неправильно по той же причине, что и здесь .
Показать ещё 1 комментарий
8

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

Еще один вариант:

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

сортирует по возрастанию по умолчанию.

  • 0
    Это решение делает для чистого кода. Работает отлично.
  • 1
    Слегка измененная версия для сортировки по нескольким полям приведена здесь при необходимости: stackoverflow.com/questions/6913512/…
7

В соответствии с вашим примером вам нужно отсортировать по двум полям (фамилия, имя), а не по одному. Вы можете использовать Alasql библиотеку, чтобы сделать этот вид в одной строке:

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

Попробуйте этот пример в jsFiddle.

7

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

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

Я не видел, чтобы этот конкретный подход предлагался, поэтому здесь используется краткий метод сравнения, который мне нравится использовать как для 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) => -(fn(a) < fn(b)) || +(fn(a) > fn(b))
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) => -(fn(a) < fn(b)) || +(fn(a) > fn(b))

сравнить их.

Вспоминая, что fn = o => o.last_nom, мы можем расширить функцию сравнения до эквивалентного

(a, b) => -(a.last_nom < b.last_nom) || +(a.last_nom > b.last_nom)

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

if (a.last_nom < b.last_nom) return -1
return +(a.last_nom > b.last_nom)

Поэтому, если a < b мы возвращаем -1, в противном случае, если a > b то мы возвращаем +1, но если a == b, то a < b и a > b являются ложными, поэтому он возвращает +0.

В качестве дополнительного бонуса здесь эквивалент в ECMAScript 5.1 без функций стрелок, который, к сожалению, не совсем такой же краткий:

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) {
    return -(fn(a) < fn(b)) || +(fn(a) > fn(b))
  }
}

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

objs.sort(sortByLastName)
console.log(objs.map(getLastName))
6
objs.sort(function(a,b){return b.last_nom>a.last_nom})
  • 1
    На самом деле это не сработало, пришлось использовать принятый ответ. Это не было правильно сортировать.
6
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
    Пожалуйста, отредактируйте ваш пост, чтобы добавить больше объяснений о том, что делает ваш код и почему он решит проблему. Ответ, который в основном содержит только код (даже если он работает), обычно не помогает ОП понять их проблему.
6

Вам может потребоваться преобразовать их в нижний регистр, чтобы предотвратить путаницу.

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

})
5

Используя Ramda,

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

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

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');
  • 4
    Как реализовать быструю сортировку в js - простое решение? Простой алгоритм, но не простое решение.
  • 0
    Это просто, поскольку не использует никаких внешних библиотек и не меняет прототип объекта. На мой взгляд, длина кода не имеет прямого влияния на сложность кода
Показать ещё 1 комментарий
4

Учитывая исходный пример:

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 над first_nom.
  • Вычитание работает в числовых полях: var age_order = left.age - right.age;
  • Отменить в обратном порядке, return -last_nom_order || -first_nom_order || -age_order; return -last_nom_order || -first_nom_order || -age_order;
4

Я просто увеличил динамическую сортировку Ege Özcan для погружения глубоко внутри объектов. Если данные выглядят следующим образом:

obj = [
    {
        a: { a: 1, b: 2, c: 3 },
        b: { a: 4, b: 5, c: 6 }
    },
    { 
        a: { a: 3, b: 2, c: 1 },
        b: { a: 6, b: 5, c: 4 }
}];

и если вы хотите отсортировать его по свойствам a.a, я думаю, что мое улучшение помогает очень хорошо. Я добавляю новую функциональность для таких объектов:

Object.defineProperty(Object.prototype, 'deepVal', {
    enumerable: false,
    writable: true,
    value: function (propertyChain) {
        var levels = propertyChain.split('.');
        parent = this;
        for (var i = 0; i < levels.length; i++) {
            if (!parent[levels[i]])
                return undefined;
            parent = parent[levels[i]];
        }
        return parent;
    }
});

и изменена функция _dynamicSort:

return function (a,b) {
        var result = ((a.deepVal(property) > b.deepVal(property)) - (a.deepVal(property) < b.deepVal(property)));
        return result * sortOrder;
    }

И теперь вы можете сортировать по a.a. следующим образом:

obj.sortBy('a.a');

См. Commplete script в JSFiddle

3

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

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

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

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

аргументы

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

Возвращает

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


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

Способ 1:

Вы можете использовать Underscore.js. Сначала импортируйте знак подчеркивания.

 import * as _ from 'underscore';
 let SortedObjs = _.sortBy(objs, 'last_nom');

Способ 2: Использовать функцию сравнения.

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

objs.sort(compare);
3

Используя lodash или Underscore, его кусок пирога

> const sortedList = _.orderBy(objs, [last_nom], [asc]); // asc or desc
2

Меня устраивает. Здесь он будет оставаться неопределенным до конца.

 function sort(items, property, direction) {

    function compare(a, b) {
      if(!a[property] && !b[property]) {
        return 0;
      } else if(a[property] && !b[property]) {
        return -1;
      } else if(!a[property] && b[property]) {
        return 1;
      } else {
        const value1 = a[property].toString().toUpperCase(); // ignore upper and lowercase
        const value2 = b[property].toString().toUpperCase(); // ignore upper and lowercase
        if (value1 < value2) {
          return direction === 0 ? -1 : 1;
        } else if (value1 > value2) {
          return direction === 0 ? 1 : -1;
        } else {
          return 0;
        }
        
      }
    }
    
    return items.sort(compare);
   } 
   
   var items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: undefined, value: -12 },
  { name: 'Magnetic', value: 13 },
  { name: 'Zeros', value: 37 }
];
   console.log('Ascending Order:- ');
   console.log(sort(items, 'name', 0));
   console.log('Decending Order:- ');
   console.log(sort(items, 'name', 1));
    
    
2

Простая функция, сортирующая массив объекта по свойству

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
2

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

function smoothSort(items,prop,reverse) {  
    var length = items.length;
    for (var i = (length - 1); i >= 0; i--) {
        //Number of passes
        for (var j = (length - i); j > 0; j--) {
            //Compare the adjacent positions
            if(reverse){
              if (items[j][prop] > items[j - 1][prop]) {
                //Swap the numbers
                var tmp = items[j];
                items[j] = items[j - 1];
                items[j - 1] = tmp;
            }
            }

            if(!reverse){
              if (items[j][prop] < items[j - 1][prop]) {
                  //Swap the numbers
                  var tmp = items[j];
                  items[j] = items[j - 1];
                  items[j - 1] = tmp;
              }
            }
        }
    }

    return items;
}
  • первые элементы аргумента - это массив объектов,

  • prop - это ключ объекта, который вы хотите отсортировать,

  • reverse - это логический параметр, который, будучи истинным, возвращает в порядке возрастания, а false - в порядке убывания.

2

Это будет сортировать два уровня вложенного массива с помощью свойства, переданного ему в алфавитном порядке.

function sortArrayObjectsByPropAlphaNum(property) {
    return function (a,b) {
        var reA = /[^a-zA-Z]/g;
        var reN = /[^0-9]/g;
        var aA = a[property].replace(reA, '');
        var bA = b[property].replace(reA, '');

        if(aA === bA) {
            var aN = parseInt(a[property].replace(reN, ''), 10);
            var bN = parseInt(b[property].replace(reN, ''), 10);
            return aN === bN ? 0 : aN > bN ? 1 : -1;
        } else {
            return a[property] > b[property] ? 1 : -1;
        }
    };
}

Использование:

objs.sort(utils.sortArrayObjectsByPropAlphaNum('last_nom'));
2

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

it 'используется путем вызова sortPeoples ([' array ',' of ',' properties '], reverse = false)

///////////////////////example array of peoples ///////////////////////

var peoples = [
    {name: "Zach", surname: "Emergency", age: 1},
    {name: "Nancy", surname: "Nurse", age: 1},
    {name: "Ethel", surname: "Emergency", age: 1},
    {name: "Nina", surname: "Nurse", age: 42},
    {name: "Anthony", surname: "Emergency", age: 42},
    {name: "Nina", surname: "Nurse", age: 32},
    {name: "Ed", surname: "Emergency", age: 28},
    {name: "Peter", surname: "Physician", age: 58},
    {name: "Al", surname: "Emergency", age: 58},
    {name: "Ruth", surname: "Registration", age: 62},
    {name: "Ed", surname: "Emergency", age: 38},
    {name: "Tammy", surname: "Triage", age: 29},
    {name: "Alan", surname: "Emergency", age: 60},
    {name: "Nina", surname: "Nurse", age: 58}
];



//////////////////////// Sorting function /////////////////////
function sortPeoples(propertyArr, reverse) {
        function compare(a,b) {
            var i=0;
            while (propertyArr[i]) {
                if (a[propertyArr[i]] < b[propertyArr[i]])  return -1;
                if (a[propertyArr[i]] > b[propertyArr[i]])  return 1;
                i++;
            }
            return 0;
            }
        peoples.sort(compare);
        if (reverse){
            peoples.reverse();
        }
    };

////////////////end of sorting method///////////////
function printPeoples(){
  $('#output').html('');
peoples.forEach( function(person){
 $('#output').append(person.surname+" "+person.name+" "+person.age+"<br>");
} )
}
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
  <html>
  <body>
<button onclick="sortPeoples(['surname']); printPeoples()">sort by ONLY by surname ASC results in mess with same name cases</button><br>
<button onclick="sortPeoples(['surname', 'name'], true); printPeoples()">sort by surname then name DESC</button><br>
<button onclick="sortPeoples(['age']); printPeoples()">sort by AGE ASC. Same issue as in first case</button><br>
<button onclick="sortPeoples(['age', 'surname']); printPeoples()">sort by AGE and Surname ASC. Adding second field fixed it.</button><br>
        
    <div id="output"></div>
    </body>
  </html>
  • 3
    массив людей с :(
2

Использование xPrototype: https://github.com/reduardo7/xPrototype/blob/master/README.md#sortbycol1-col2-coln

var o = [ 
  { Name: 'Lazslo', LastName: 'Jamf'     },
  { Name: 'Pig',    LastName: 'Bodine'   },
  { Name: 'Pirate', LastName: 'Prentice' },
  { Name: 'Pag',    LastName: 'Bodine'   }
];


// Original
o.each(function (a, b) { console.log(a, b); });
/*
 0 Object {Name: "Lazslo", LastName: "Jamf"}
 1 Object {Name: "Pig", LastName: "Bodine"}
 2 Object {Name: "Pirate", LastName: "Prentice"}
 3 Object {Name: "Pag", LastName: "Bodine"}
*/


// Sort By LastName ASC, Name ASC
o.sortBy('LastName', 'Name').each(function(a, b) { console.log(a, b); });
/*
 0 Object {Name: "Pag", LastName: "Bodine"}
 1 Object {Name: "Pig", LastName: "Bodine"}
 2 Object {Name: "Lazslo", LastName: "Jamf"}
 3 Object {Name: "Pirate", LastName: "Prentice"}
*/


// Sort by LastName ASC and Name ASC
o.sortBy('LastName'.asc, 'Name'.asc).each(function(a, b) { console.log(a, b); });
/*
 0 Object {Name: "Pag", LastName: "Bodine"}
 1 Object {Name: "Pig", LastName: "Bodine"}
 2 Object {Name: "Lazslo", LastName: "Jamf"}
 3 Object {Name: "Pirate", LastName: "Prentice"}
*/


// Sort by LastName DESC and Name DESC
o.sortBy('LastName'.desc, 'Name'.desc).each(function(a, b) { console.log(a, b); });
/*
 0 Object {Name: "Pirate", LastName: "Prentice"}
 1 Object {Name: "Lazslo", LastName: "Jamf"}
 2 Object {Name: "Pig", LastName: "Bodine"}
 3 Object {Name: "Pag", LastName: "Bodine"}
*/


// Sort by LastName DESC and Name ASC
o.sortBy('LastName'.desc, 'Name'.asc).each(function(a, b) { console.log(a, b); });
/*
 0 Object {Name: "Pirate", LastName: "Prentice"}
 1 Object {Name: "Lazslo", LastName: "Jamf"}
 2 Object {Name: "Pag", LastName: "Bodine"}
 3 Object {Name: "Pig", LastName: "Bodine"}
*/
1

Я дам вам решение, реализующее алгоритм selectionSort, он прост и эффективен

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


function selection_Sort(num) { 
 //console.log(num);  
 var temp, index;
 for (var i = 0; i <= num.length - 1; i++) {
index = i;
for (var j = i + 1; j <= num.length - 1; j++) {
 // you can use first_nom/last_nom,any way you choose to sort

  if (num[j]. last_nom < num[index]. last_nom) {
    index = j;
  } 
}

//below is the swapping part
temp = num[i]. last_nom;
num[i]. last_nom = num[index]. last_nom;
num[index]. last_nom = temp;
 };
 console.log(num); 
 return num; 
  }
  selection_Sort(objs);

Замечательно видеть такие замечательные ответы

1
// Sort Array of Objects

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

// Property to Sort By
var args = "last_nom";

// Function to Sort the Data by given Property
function sortByProperty(property) {
    return function (a, b) {
        var sortStatus = 0,
            aProp = a[property].toLowerCase(),
            bProp = b[property].toLowerCase();
        if (aProp < bProp) {
            sortStatus = -1;
        } else if (aProp > bProp) {
            sortStatus = 1;
        }
        return sortStatus;
    };
}

// Implementation
var sortedArray = booksArray.sort(sortByProperty(args));

console.log("sortedArray: " + JSON.stringify(sortedArray) );

Выход журнала консоли:

"sortedArray: 
[{"first_nom":"Pig","last_nom":"Bodine"},
{"first_nom":"Lazslo","last_nom":"Jamf"},
{"first_nom":"Pirate","last_nom":"Prentice"}]"

Адаптировано на основе этого источника: http://www.levihackwith.com/code-snippet-how-to-sort-an-array-of-json-objects-by-property/

Ещё вопросы

Сообщество Overcoder
Наверх
Меню