javascript sort Ordenar la matriz de objetos por valor de propiedad de cadena




sort object javascript (24)

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?

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


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

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

Hay muchas respuestas buenas aquí, pero me gustaría señalar que se pueden extender muy simplemente para lograr una clasificación mucho más compleja. Lo único que tienes 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) )

Donde fn1 , fn1 , ... 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 la primera expresión evaluada que se puede convertir en verdadero .

La forma más 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) )

Teniendo dos pasos con last_nom , el first_nom 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 de caso, tipos de datos arbitrarios, etc.

Puedes usarlos 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 el JavaScript puro con enfoque funcional puede llevarlo lejos sin bibliotecas externas o código complejo. También es muy efectivo, ya que no se debe hacer un análisis de cadena.


Clasificación (más) matrices complejas de objetos

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

TL; DR

Son versiones más conectables basadas en la answer muy bonita de @ege-Özcan .

Problema

Encontré lo de abajo y no pude cambiarlo. Tampoco quise aplanar el objeto temporalmente. Tampoco quise utilizar el guión bajo / lodash, principalmente por motivos de rendimiento y la diversión para 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"}
];

Gol

El objetivo es clasificarlo principalmente por People.Name.name y, en segundo lugar, por People.Name.surname

Obstáculos

Ahora, en la solución base se usa la notación de corchetes para calcular las propiedades para ordenar dinámicamente. Sin embargo, aquí también tendríamos que construir dinámicamente la notación de corchetes, ya que uno esperaría que People['Name.name'] como People['Name.name'] funcionen, lo que no funciona.

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

Solución

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

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://.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


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

También puede crear una función de ordenación dinámica que ordene los objetos por el valor que pase:

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

Así que puedes tener una matriz de objetos como este:

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 parte inferior está escrita porque muchas personas se contactaron conmigo, quejándose de que no funciona con múltiples parámetros .

Parámetros múltiples

Puede utilizar la siguiente función para generar funciones de clasificación con múltiples 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 te permitiría hacer algo como esto:

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

Añadiéndolo al prototipo

(La implementación que está justo debajo está inspirada en la answer de Mike R )

No recomendaría cambiar un prototipo de objeto nativo, pero solo para dar un ejemplo para que pueda implementarlo en sus propios objetos (para los entornos que lo admiten, también puede usar Object.defineProperty como se muestra en la siguiente sección, que al menos no tiene el efecto secundario negativo de ser enumerable, como se describe en la última parte)

La implementación de prototipos sería algo como lo siguiente ( Aquí hay un ejemplo de trabajo ):

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

La forma "OK" de agregarlo al prototipo

Si está apuntando a IE v9.0 y más, entonces, como mencioné anteriormente, use Object.defineProperty como este ( ejemplo funcional ):

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

Esto puede ser un compromiso aceptable hasta que llegue el operador de vinculación.

Toda esa diversión prototipo lo habilita:

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

Deberías leer esto

Si usa el método de acceso directo de prototipo (Object.defineProperty está bien) y el otro código no comprueba hasOwnProperty , ¡los gatitos mueren! Ok, para ser honesto, ningún gatito le hace daño realmente, pero probablemente las cosas se romperán y todos los demás desarrolladores de tu equipo te odiarán:

¿Ves ese último "SortBy"? Sí. No es genial Use Object.defineProperty donde pueda, y deje el Array.prototype solo de lo contrario.


underscore.js

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

sortBy_.sortBy (lista, iterador, [contexto]) Devuelve una copia ordenada de la lista, clasificada en orden ascendente por los resultados de la ejecución de cada valor a través del iterador. El iterador también puede ser el nombre de la cadena de la propiedad por la que se ordena (por ejemplo, la 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' );

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)está universalmente compatible y vuelve -1,0,1 si a<b, a==b, a>brespectivamente.
  • ||En la última línea da last_nomprioridad a más first_nom.
  • La resta funciona en campos numéricos: var age_order = left.age - right.age;
  • Negar para revertir el orden, return -last_nom_order || -first_nom_order || -age_order;

Puedes usar

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 las iteraciones para ordenar. Si las órdenes no se especifican, todos los valores se ordenan en orden ascendente. De lo contrario, especifique un orden de "desc" para descendente o "asc" para el orden ascendente de los valores correspondientes.

Argumentos

collection (Array | Object): la colección para iterar sobre. [iteratees = [_. identity]] (Array [] | Function [] | Object [] | string []): el iterar para ordenar. [órdenes] (cadena []): los ordenamientos de iteraciones.

Devoluciones

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

Una forma sencilla:

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

Vea que '.toLowerCase()' es necesario para evitar errores al comparar cadenas.


Es bastante fácil escribir tu 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)); 

Solución simple y rápida a este problema usando la herencia del prototipo:

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

Actualización: ya no modifica la matriz original.


Siguiendo su ejemplo, necesita ordenar por dos campos (apellido, primer nombre), en lugar de uno. Puede usar la biblioteca Alasql 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 .


Usando lodash o subrayado, es un pedazo de pastel

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

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

})

No he visto este enfoque en particular sugerido, así que aquí hay un método de comparación conciso que me gusta usar que funciona tanto para la string como para el 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))

Aquí hay una explicación de sortBy() :

sortBy() acepta un fn que selecciona qué valor de un objeto para usar como comparación, y devuelve una función que se puede pasar directamente a Array.prototype.sort() .En este ejemplo, estamos usando o.last_nomel valor de comparación, por lo que cada vez que recibimos dos objetos a través de Array.prototype.sort(), como

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

y

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

usamos

(a, b) => -(fn(a) < fn(b)) || +(fn(a) > fn(b))

para compararlos

Recordando eso fn = o => o.last_nom, podemos expandir la función de comparación al equivalente

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

El ||operador lógico OR tiene una funcionalidad de cortocircuito que es muy útil aquí. Debido a cómo funciona, el cuerpo de la función anterior significa

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

Entonces, si a < bregresamos -1, de lo contrario, si a > bluego regresamos +1, pero si a == b, entonces a < by a > bsomos falsos, entonces regresa +0.

Como un bono adicional, aquí está el equivalente en ECMAScript 5.1 sin funciones de flecha, que desafortunadamente no es tan conciso:

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


Lodash.js (superconjunto de Underscore.js )

Es bueno no agregar un marco para cada pieza simple de lógica, pero confiar en un marco de utilidad bien probado, acelerar el desarrollo y reducir la cantidad de errores escritos no es una vergüenza.

Lodash produce código muy limpio y promueve un estilo de programación más funcional , lo que resulta en menos errores. En 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 _.property shorthand 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 revertirlo? No hay problema. Utilice _.reverse .

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

¿Quieres combinar ambos usando Chaining lugar?

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

Acabo de mejorar el ordenamiento dinámico de Ege Özcan para sumergirse profundamente en los objetos. Si Data se parece a esto:

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

y si desea clasificarlo en una propiedad, creo que mi mejora es muy útil. Añado nuevas funcionalidades a objetos como este:

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

y cambió la función de retorno de _dynamicSort :

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

Y ahora puedes ordenar por aa de esta manera:

obj.sortBy('a.a');

Ver Secuencias de comandos completas en JSFiddle


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

}

A partir de 2018 existe una solución mucho más corta y elegante. Solo usa. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… .

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

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 el ascenso por defecto.


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

Tengo un código que me funciona:

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

ACTUALIZACIÓN: No funciona siempre, por lo que no es correcto :(


Combinando la solución dinámica de Ege con la idea de Vinay, obtiene 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");

En ES6 / ES2015 o posterior puedes hacer esto:

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




sorting