Ordenar matriz de objetos por valor de propiedad de cadena

Tengo una matriz de objetos JavaScript:

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

¿Cómo puedo ordenarlos por el valor de last_nom en JavaScript?

Se acerca de sort(a,b), pero eso solo parece funcionar con cadenas y números. ¿Necesito agregar un toString() método a mis objetos?

preguntado el 15 de julio de 09 a las 00:07

Este script le permite hacer precisamente eso a menos que desee escribir su propia función de comparación o clasificador: thomasfrank.se/sorting_things.html -

la forma más rápida es usar el isomorfo matriz de clasificación módulo que funciona de forma nativa tanto en el navegador como en el nodo, y admite cualquier tipo de entrada, campos calculados y órdenes de clasificación personalizadas. -

30 Respuestas

Es bastante fácil escribir su propia función de comparación:

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

objs.sort( compare );

O en línea (c / o Marco Demaio):

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

Respondido el 20 de enero de 21 a las 22:01

O en línea: objs.sort (function (a, b) {return (a.last_nom> b.last_nom)? 1: ((b.last_nom> a.last_nom)? -1: 0);}); - marco demaio

return a.last_nom.localeCompare(b.last_nom) también funcionará. - cerbro

para aquellos que buscan una ordenación en la que el campo sea numérico, el cuerpo de la función de comparación: return a.value - b.value; (ASC)- André Figueiredo

También puede crear una función de clasificación dinámica que clasifique los objetos por su valor que pasa:

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

Entonces puedes tener una matriz de objetos como esta:

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

... y funcionará cuando lo hagas:

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

En realidad, esto ya responde a la pregunta. La siguiente parte está escrita porque mucha gente se puso en contacto conmigo, quejándose de que no funciona con múltiples parámetros.

Múltiples parámetros

Puede utilizar la función siguiente para generar funciones de clasificación con varios parámetros de clasificación.

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

Lo que le permitiría hacer algo como esto:

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

Matriz de subclases

Para los afortunados que pueden usar ES6, que permite extender los objetos nativos:

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

Eso permitiría esto:

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

Respondido el 13 de diciembre de 20 a las 09:12

En ES6 / ES2015 o posterior, puede hacerlo de esta manera:

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

Antes de ES6 / ES2015

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

respondido 08 nov., 19:17

esto ha estado disponible desde JS 1.1, la pieza de flecha gruesa para esto es la pieza ES6 / 2015. Pero sigue siendo muy útil, y la mejor respuesta en mi opinión: jon harding

@PratikKelwalkar: si necesita revertirlo, simplemente cambie la comparación ayb: objs.sort ((a, b) => b.last_nom.localeCompare (a.last_nom)); - Vlad Bezden

underscore.js

use subrayado, es pequeño e impresionante ...

sortBy_.sortBy (lista, iterador, [contexto]) Devuelve una copia ordenada de la lista, clasificada en orden ascendente según los resultados de ejecutar cada valor a través del iterador. Iterador también puede ser el nombre de cadena de la propiedad por ordenar (por ejemplo, longitud).

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

respondido 10 mar '16, 19:03

David, ¿podrías editar la respuesta para decir, var sortedObjs = _.sortBy( objs, 'first_nom' );. objs seguirá no clasificarse a sí mismo como resultado de esto. La función retorno una matriz ordenada. Eso lo haría más explícito. - Cadena

Para revertir la ordenación: var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse(); - Erdal G.

debe cargar la biblioteca de JavaScript "subrayado": <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"> </script> - y-bri

También está disponible en Lodash para los que prefieren ese - WoJ

En lodash esto sería lo mismo: var sortedObjs = _.sortBy( objs, 'first_nom' ); o si lo quieres en un orden diferente: var sortedObjs = _.orderBy( objs, ['first_nom'],['dsc'] ); - travis heeter

Si tiene apellidos duplicados, puede ordenarlos por nombre-

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

Respondido 29 Abr '14, 19:04

@BadFeelingAboutThis, ¿qué significa devolver -1 o 1? Entiendo que -1 significa literalmente que A es menor que B solo por la sintaxis, pero ¿por qué usar un 1 o -1? Veo que todos están usando esos números como valores de retorno, pero ¿por qué? Gracias. - Chris22

@ Chris22 un número negativo devuelto significa que b debería venir después a en la matriz. Si se devuelve un número positivo, significa a debería venir después b. Si 0 se devuelve, significa que se consideran iguales. Siempre puedes leer la documentación: desarrollador.mozilla.org/en-US/docs/Web/JavaScript/Reference/… - Mal sentimiento sobre esto

@BadFeelingAboutThis gracias, por la explicación y el enlace. Lo crea o no, busqué en Google varios fragmentos de código utilizando el 1, 0, -1 antes de preguntar esto aquí. Simplemente no estaba encontrando la información que necesitaba. - Chris22

Solución simple y rápida a este problema mediante la herencia de prototipos:

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

Ejemplo / uso

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

Actualizar: Ya no modifica la matriz original.

contestado el 15 de mayo de 15 a las 22:05

No devuelve simplemente otra matriz. ¡pero en realidad ordena el original !. - Vinay Aggarwal

Si desea asegurarse de que está utilizando una ordenación natural con números (es decir, 0,1,2,10,11, etc.) utilice parseInt con el conjunto Radix. desarrollador.mozilla.org/en-US/docs/Web/JavaScript/Reference/… entonces: return (parseInt (a [p], 10)> parseInt (b [p], 10))? 1: (parseInt (a [p], 10) <parseInt (b [p], 10))? -1: 0; - Paul

@codehuntr Gracias por corregirlo. pero supongo que en lugar de hacer una función de clasificación para hacer esta sensibilización, es mejor si hacemos una función separada para corregir los tipos de datos. Porque la función de clasificación no puede no decir qué propiedad contendrá qué tipo de datos. :) - Vinay Aggarwal

Muy agradable. Invierta las flechas para obtener el efecto asc / desc. - Abdul Sadik Yalcin

nunca, nunca sugiera una solución que modifique el prototipo de un objeto central - pbanka

A partir de 2018 hay una solución mucho más corta y elegante. Solo usa. Array.prototype.sort ().

Ejemplo:

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

Respondido el 04 de junio de 18 a las 16:06

En la pregunta, las cadenas se utilizaron para la comparación en lugar de los números. Su respuesta funciona muy bien para ordenar por números, pero no es tan buena para las comparaciones por cadena. - smcstewart

La a.value - b.value utilizado para comparar los atributos del objeto (números en este caso) se puede adoptar para los distintos tiempos de datos. Por ejemplo, la expresión regular se puede utilizar para comparar cada par de vecinos instrumentos de cuerda. - 0leg

@ 0leg Me gustaría ver un ejemplo aquí del uso de expresiones regulares para comparar cadenas de esta manera. - bob stein

Esta implementación es bastante buena si necesita ordenarla por ID. Sí, ha sugerido usar regex para comparar la cadena vecina, lo que hace que la solución sea más complicada, mientras que el propósito de esta versión simplificada será diferente si se usa regex junto con la solución dada. La simplicidad es lo mejor. - surendrapanday

Respuesta anterior que no es correcta:

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

ACTUALIZACIÓN

Del comentario de Beauchamp:

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

Formato más legible:

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

Sin ternaries anidados:

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

Explicación: Number() lanzará true a 1 y false a 0.

Respondido el 24 de diciembre de 19 a las 21:12

Funciona, pero el resultado es inestable por alguna razón: TitanFighter

@ AO17 no, no lo hará. No puedes restar cadenas. - Patrick Roberts

Esto debería hacerlo: arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0)) - Jean-François Beauchamp

@ Jean-FrançoisBeauchamp, su solución funciona perfectamente bien y mucho mejor. - Ahsan

¡el tercero con Number es sencillo y agradable! - Kojo

Lodash.js (superconjunto de Underscore.js)

Es bueno no agregar un marco para cada simple pieza de lógica, pero confiar en marcos de utilidad bien probados puede acelerar el desarrollo y reducir la cantidad de errores.

Lodash produce un código muy limpio y promueve una mayor programación funcional estilo. De un vistazo, queda claro cuál es la intención del código.

El problema de OP se puede resolver simplemente como:

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

¿Más información? Por ejemplo, tenemos el siguiente objeto anidado:

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

Ahora podemos usar el _.propiedad taquigrafía user.age para especificar la ruta a la propiedad que debe coincidir. Ordenaremos los objetos de usuario por la propiedad de edad anidada. Sí, permite la coincidencia de propiedades anidadas.

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

¿Quieres que se invierta? No hay problema. Usar _.contrarrestar.

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

Quiere combinar ambos usando cadena?

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

O cuando prefieres flujo sobre cadena

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

respondido 04 mar '20, 09:03

No he visto sugerido este enfoque en particular, así que aquí hay un método de comparación conciso que me gusta usar y que funciona para ambos string y number tipos:

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

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

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

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

Explicación de sortBy()

sortBy() acepta un fn que selecciona un valor de un objeto para usar en comparación, y devuelve una función que se puede pasar a Array.prototype.sort(). En este ejemplo, comparamos o.last_nom. Siempre que recibamos dos objetos como

a = { first_nom: 'Lazslo', last_nom: 'Jamf' }
b = { first_nom: 'Pig', last_nom: 'Bodine' }

los comparamos con (a, b) => cmp(fn(a), fn(b)). Dado que

fn = o => o.last_nom

podemos expandir la función de comparación a (a, b) => cmp(a.last_nom, b.last_nom). Por la forma OR lógico (||) funciona en JavaScript, cmp(a.last_nom, b.last_nom) es equivalente a

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

Por cierto, esto se llama comparación de tres vías "nave espacial" (<=>) operador en otros idiomas

Finalmente, aquí está la sintaxis compatible con ES5 sin usar funciones de flecha:

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

function sortBy(fn) {
  function cmp(a, b) { return -(a < b) || +(a > b); }
  return function (a, b) { return cmp(fn(a), fn(b)); };
}

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

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

Respondido 09 Jul 20, 16:07

Me gusta este enfoque, pero creo que usar la abreviatura de -(fa < fb) || +(fa > fb) es un error aquí. Son varias declaraciones condensadas en una línea de código. La alternativa, escrita con un if declaración, sería mucho más legible sin dejar de ser bastante concisa. Creo que es un error sacrificar la legibilidad por la belleza. - MSOACC

@MSOACC gracias por tu opinión pero respetuosamente no estoy de acuerdo. Otros lenguajes implementan un operador de comparación de tres vías que realiza la misma comparación, así que piense en ello conceptualmente como fa <=> fb. - Patrick Roberts

Hola Patrick, me gusta tu respuesta, pero funcionaría correctamente solo con caracteres en inglés (const cmp = (a, b) => -(a < b) || +(a > b);) Pensar en ["ä", "a", "c", "b"].sort(cmp) => ["a", "b", "c", "ä"], Donde ä se empuja hasta el final. En su lugar, probablemente debería actualizar la función de comparación a: const cmp = (a, b) => a.localeCompare(b); => ["a", "ä", "b", "c"] Saludos y gracias por la respuesta ;-) - rjanjic

@rjanjic gracias por los comentarios. Soy consciente de que se ordena según el punto de código del carácter en Unicode. Sin embargo, cambiarlo para usar localeCompare elimina la capacidad de ordenar números, y también es significativamente Más lento. - Patrick Roberts

En lugar de utilizar una función de comparación personalizada, también puede crear un tipo de objeto con toString() método (que es invocado por la función de comparación predeterminada):

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

Respondido 15 Jul 09, 08:07

Puedes usar

La manera más fácil: Lodash

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

Este método es como _.sortBy, excepto que permite especificar los órdenes de clasificación de los iterados por los que ordenar. Si no se especifica pedidos, todos los valores se ordenan en orden ascendente. De lo contrario, especifique un orden de "desc" para descender o "asc" para ascender el orden de clasificación de los valores correspondientes.

Argumentos

colección (Array | Object): la colección sobre la que se iterará. [iteratees = [_. identity]] (Array [] | Función [] | Objeto [] | cadena []): Las iteraciones por ordenar. [pedidos] (cadena []): el orden de clasificación de los iterados.

Retorno

(Matriz): devuelve la nueva matriz ordenada.


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

Respondido 23 ago 18, 15:08

Aquí hay muchas respuestas buenas, pero me gustaría señalar que se pueden extender de manera muy simple para lograr una clasificación mucho más compleja. Lo único que tiene que hacer es usar el operador OR para encadenar funciones de comparación como esta:

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

Dónde fn1, fn2, ... son las funciones de ordenación que devuelven [-1,0,1]. Esto da como resultado "ordenar por fn1", "ordenar por fn2" que es prácticamente igual a ORDER BY en SQL.

Esta solución se basa en el comportamiento de || operador que evalúa al primera expresión evaluada que se puede convertir en verdadera.

La forma mas simple tiene solo una función en línea como esta:

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

Tener dos pasos con last_nom,first_nom el orden de clasificación se vería así:

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

Una función de comparación genérica podría ser algo como esto:

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

Esta función podría ampliarse para admitir campos numéricos, sensibilidad entre mayúsculas y minúsculas, tipos de datos arbitrarios, etc.

Puede usarlo encadenándolos por orden de prioridad:

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

El punto aquí es que JavaScript puro con un enfoque funcional puede llevarlo muy lejos sin bibliotecas externas o código complejo. También es muy eficaz, ya que no es necesario realizar un análisis de cadenas.

contestado el 05 de mayo de 16 a las 12:05

Ejemplo de uso:

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

Guión:

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

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

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

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

respondido 21 mar '19, 17:03

gracias por desglosar esto, estoy tratando de entender por qué los dígitos 1, 0, -1 se utilizan para ordenarlos. Incluso con su explicación anterior, que se ve muy bien, todavía no la entiendo del todo. Siempre pienso en -1 como cuando se usa la propiedad de longitud de matriz, es decir: arr.length = -1 significa que el elemento no se encuentra. Probablemente estoy mezclando cosas aquí, pero ¿podrías ayudarme a entender por qué los dígitos 1, 0, -1 se utilizan para determinar el orden? Gracias. - Chris22

Esto no es enteramente exacto, pero podría ayudar pensarlo así: la función pasada a array.sort se llama una vez para cada elemento de la matriz, como el argumento denominado "a". El valor de retorno de cada llamada de función es cómo se debe alterar el índice (número de posición actual) del elemento "a" en comparación con el siguiente elemento "b". El índice dicta el orden de la matriz (0, 1, 2, etc.) Entonces, si "a" está en el índice 5 y devuelve -1, entonces 5 + -1 == 4 (muévalo más cerca del frente) 5 + 0 == 5 (manténgalo donde está) etc. Recorre la matriz comparando 2 vecinos cada vez hasta que llega al final, dejando una matriz ordenada. - jaime mason

gracias por tomarse el tiempo para explicar esto más a fondo. Entonces, usando tu explicación y la MDN Array.prototype.sort, Te diré lo que estoy pensando de esto: en comparación con a y b, Si a es mayor que b suma 1 al índice de a y colócalo detrás b, Si a es menor que b, restar 1 de a y colóquelo frente a b. Si a y b son iguales, agregue 0 a a y déjalo donde está. - Chris22

Prueba esto,

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

respondido 03 mar '20, 08:03

la mejor y más fácil solución - omar hasan

Sé que esta pregunta es demasiado antigua, pero no vi ninguna implementación similar a la mía.
Esta versión se basa en el Modismo de transformación de Schwartzian.

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

Aquí tienes un ejemplo de cómo usarlo:

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

contestado el 06 de mayo de 20 a las 09:05

Ordenar (más) matrices complejas de objetos

Dado que probablemente encuentre estructuras de datos más complejas como esta matriz, ampliaría la solución.

TL; DR

Son versiones más conectables basadas en @ ege-Özcanes muy lindo https://www.youtube.com/watch?v=xB-eutXNUMXJtA&feature=youtu.be.

Problema

Encontré lo siguiente y no pude cambiarlo. Tampoco quería aplanar el objeto temporalmente. Tampoco quería usar subrayado / lodash, principalmente por razones de rendimiento y la diversión de implementarlo yo mismo.

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

Objetivo

El objetivo es ordenarlo principalmente por People.Name.name y secundariamente por People.Name.surname

Obstáculos

Ahora, en la solución base, se usa la notación entre corchetes para calcular las propiedades para ordenar dinámicamente. Aquí, sin embargo, también tendríamos que construir la notación de corchetes dinámicamente, ya que esperaría algo como People['Name.name'] funcionaría, lo cual no es así.

Simplemente haciendo People['Name']['name'], por otro lado, es estático y solo te permite bajar por el n-ésimo nivel.

Solución

La principal adición aquí será caminar por el árbol de objetos y determinar el valor de la última hoja, debe especificar, así como cualquier hoja intermediaria.

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

Ejemplo

Ejemplo de trabajo en JSBin

contestado el 23 de mayo de 17 a las 13:05

¿Por qué? Esta no es la respuesta a la pregunta original y "el objetivo" podría resolverse simplemente con People.sort ((a, b) => {return a.Name.name.localeCompare (b.Name.name) || a.Name .surname.localeCompare (b.Name.surname)}) - tero tolonen

Una opción más:

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

ordena ascendente por defecto.

Respondido el 26 de junio de 16 a las 10:06

La versión ligeramente modificada para ordenar por múltiples campos está aquí si es necesario: stackoverflow.com/questions/6913512/… - Ravshan Samandárov

parece que podría ser el siguiente: función de exportación generateSortFn (prop: string, reverse: boolean = false): (... args: any) => number {return (a, b) => {return a [prop ] <b [prop]? contrarrestar ? 1: -1: a [prop]> b [prop]? contrarrestar ? -1: 1: 0; }; } - guarida kerny

de acuerdo, pero en algunos casos no tengo la necesidad de mirar las funciones de utilidad. - guarida kerny

Una forma sencilla:

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

Mira eso '.toLowerCase()' es necesario evitar errores al comparar cadenas.

Respondido el 15 de enero de 16 a las 13:01

Podrías usar funciones de flecha para dejar el código un poco más elegante: objs.sort( (a,b) => b.last_nom.toLowerCase() < a.last_nom.toLowerCase() ); - Sertaje

Esto es incorrecto por la misma razón que se explicó. aquí. - Patrick Roberts

Las funciones de flecha no son dignas de ES5. Toneladas de motores todavía están restringidas a ES5. En mi caso, encuentro la respuesta anterior significativamente mejor ya que estoy en un motor ES5 (forzado por mi empresa): dylanh724

Una función simple que clasifica una matriz de objetos por una propiedad.

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
}

Uso:

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

contestado el 28 de mayo de 18 a las 20:05

Esta solución funcionó perfectamente para mí para ordenar bidireccional. Gracias - manjuvreddy

parámetros de descripción adicionales para Ege Ozcan código

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

Respondido el 16 de Septiembre de 12 a las 21:09

Combinando la solución dinámica de Ege con la idea de Vinay, obtienes una solución robusta y agradable:

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

Uso:

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

Respondido 23 Abr '13, 17:04

¡Gracias por la idea! Por cierto, no anime a la gente a cambiar el prototipo de matriz (consulte la advertencia al final de mi ejemplo). - Ege Ozcan

Esta es mi opinión sobre esto:

La order El parámetro es opcional y por defecto es "ASC" para orden ascendente.

Trabaja en caracteres acentuados y no distingue entre mayúsculas y minúsculas.

NOTA: Ordena y devuelve el ORIGINAL formación.

function sanitizeToSort(str) {
  return str
    .normalize('NFD')                   // REMOVE ACCENTED AND DIACRITICS
    .replace(/[\u0300-\u036f]/g,'')     // REMOVE ACCENTED AND DIACRITICS
    .toLowerCase()                      // SORT WILL BE CASE INSENSITIVE
  ;
}

function sortByProperty(arr, property, order="ASC") {
  arr.forEach((item) => item.tempProp = sanitizeToSort(item[property]));
  arr.sort((a,b) => order === "ASC" ?
      a.tempProp > b.tempProp ?  1 : a.tempProp < b.tempProp ? -1 : 0
    : a.tempProp > b.tempProp ? -1 : a.tempProp < b.tempProp ?  1 : 0
  );
  arr.forEach((item) => delete item.tempProp);
  return arr;
}

SNIPPET

function sanitizeToSort(str) {
  return str
    .normalize('NFD')                   // REMOVE ACCENTED CHARS
    .replace(/[\u0300-\u036f]/g,'')     // REMOVE DIACRITICS
    .toLowerCase()
  ;
}

function sortByProperty(arr, property, order="ASC") {
  arr.forEach((item) => item.tempProp = sanitizeToSort(item[property]));
  arr.sort((a,b) => order === "ASC" ?
      a.tempProp > b.tempProp ?  1 : a.tempProp < b.tempProp ? -1 : 0
    : a.tempProp > b.tempProp ? -1 : a.tempProp < b.tempProp ?  1 : 0
  );
  arr.forEach((item) => delete item.tempProp);
  return arr;
}

const rockStars = [
  { name: "Axl",
    lastname: "Rose" },
  { name: "Elthon",
    lastname: "John" },
  { name: "Paul",
    lastname: "McCartney" },
  { name: "Lou",
    lastname: "Reed" },
  { name: "freddie",             // WORKS ON LOWER/UPPER CASE
    lastname: "mercury" },
  { name: "Ámy",                 // WORKS ON ACCENTED CHARS TOO
    lastname: "winehouse"}
  
];

sortByProperty(rockStars,"name");

console.log("Ordered by name A-Z:");
rockStars.forEach((item) => console.log(item.name + " " + item.lastname));

sortByProperty(rockStars,"lastname","DESC");

console.log("\nOrdered by lastname Z-A:");
rockStars.forEach((item) => console.log(item.lastname + ", " + item.name));

Respondido el 07 de Septiembre de 20 a las 09:09

no funciona si la lista contiene el nombre en la combinación de caracteres en mayúsculas y minúsculas - Ankesh Pandey

@AnkeshPandey Gracias por señalar eso. Lo he arreglado. - cbdesarrollador

¡Atención!
Usar esta solución es no se recomienda ya que no da como resultado una matriz ordenada. Se deja aquí para referencia futura, porque la idea no es rara.

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

Respondido el 11 de diciembre de 20 a las 12:12

En realidad, no pareció funcionar, tuve que usar la respuesta aceptada. No estaba clasificando correctamente. - locos

Según su ejemplo, debe ordenar por dos campos (apellido, nombre), en lugar de uno. Puedes usar Alasql biblioteca para hacer este tipo en una línea:

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

Prueba este ejemplo en jsFiddle.

Respondido el 18 de diciembre de 14 a las 11:12

Usando Ramda,

npm instalar 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)

Respondido 05 Jul 17, 08:07

Dado el ejemplo original:

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

Ordenar por múltiples campos:

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

Notas

  • a.localeCompare(b) is universalmente apoyado y devuelve -1,0,1 si a<b,a==b,a>b respectivamente.
  • || en la ultima linea da last_nom prioridad sobre first_nom.
  • La resta funciona en campos numéricos: var age_order = left.age - right.age;
  • Negar el orden inverso, return -last_nom_order || -first_nom_order || -age_order;

Respondido 24 Feb 18, 00:02

Es posible que deba convertirlos a minúsculas para evitar confusiones.

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

})

Respondido 14 ago 13, 11:08

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

Respondido 29 Oct 15, 13:10

Considere editar su publicación para agregar más explicaciones sobre lo que hace su código y por qué resolverá el problema. Una respuesta que en su mayoría solo contiene código (incluso si está funcionando) generalmente no ayudará al OP a comprender su problema. - Drenmi

Este es un problema simple, no sé por qué la gente tiene una solución tan compleja.
Una función de clasificación simple (basada en ordenación rápida algoritmo):

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

Ejemplo de uso:

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

respondido 19 nov., 15:14

¿Cómo implementar la ordenación rápida en js es una solución simple? Algoritmo simple pero no una solución simple. - Andrés

Es simple, ya que no usa bibliotecas externas y no cambia el prototipo del objeto. En mi opinión, la longitud del código no tiene un impacto directo en la complejidad del código: Gil Epshtain

Bueno, déjeme intentarlo con otras palabras: ¿cómo reinventar la rueda es una solución simple? - Roberto14

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.