Ordenar matriz de objetos por valor de propiedad de cadena
Frecuentes
Visto 1,830 equipos
3139
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?
30 Respuestas
4418
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
Documentos oficiales: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… - mikemaccana
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
904
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
554
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
205
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
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
72
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
55
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
50
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
46
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
40
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
34
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
32
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
31
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
26
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
24
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
23
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
22
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
15
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
14
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
11
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
11
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
10
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
10
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
10
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
10
¡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
8
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
8
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
8
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 sia<b
,a==b
,a>b
respectivamente.||
en la ultima linea dalast_nom
prioridad sobrefirst_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
7
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
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"));
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
7
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 javascript arrays sorting comparison or haz tu propia pregunta.
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 - Baversjo
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. - Lloyd