Matriz de clasificación de Javascript por dos campos


88
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);      
    return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;
});

Entonces, el código anterior ordena la matriz por gsize, de menor a mayor. Funciona bien. Pero si el tamaño de g es el mismo, me gustaría ordenarlo por brillo.

Gracias.


La función de clasificación reacciona ante un resultado positivo, negativo o cero. por lo que puede escribir: "return aSize - bSize". será un código más simple y legible.

Respuestas:


107
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);

    if(aSize == bSize)
    {
        return (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0;
    }
    else
    {
        return (aSize < bSize) ? -1 : 1;
    }
});

170
grouperArray.sort(function (a, b) {   
    return a.gsize - b.gsize || a.glow - b.glow;
});

versión más corta


gran atajo! me ayudó a armar una solución más compleja .. stackoverflow.com/questions/6101475/…
Joseph Poirier

3
¡Bonito y limpio! Lo único que solo funciona con números.
Afanasii Kurakin

¡¿Puedes explicar la lógica aquí?!. Me funcionó sort an array with a key's value firsty luegosort the result with another key's value
KTM

1
@KTM La lógica es la siguiente: si ambos gsize son iguales, entonces la primera parte de la condición es igual a 0, que se considera falsa, y se ejecuta la segunda parte de la condición.
Scalpweb

@Scalpweb Sí :) así que esto funciona para ordenar una matriz con cualquier número de claves una por una, ¿verdad? Buen truco
KTM


14

Me doy cuenta de que esto se preguntó hace algún tiempo, pero pensé en agregar mi solución.

Esta función genera métodos de clasificación de forma dinámica. simplemente proporcione el nombre de cada propiedad secundaria clasificable, precedido de +/- para indicar el orden ascendente o descendente. Súper reutilizable y no necesita saber nada sobre la estructura de datos que ha reunido. Podría hacerse a prueba de idiotas, pero no parece necesario.

function getSortMethod(){
    var _args = Array.prototype.slice.call(arguments);
    return function(a, b){
        for(var x in _args){
            var ax = a[_args[x].substring(1)];
            var bx = b[_args[x].substring(1)];
            var cx;

            ax = typeof ax == "string" ? ax.toLowerCase() : ax / 1;
            bx = typeof bx == "string" ? bx.toLowerCase() : bx / 1;

            if(_args[x].substring(0,1) == "-"){cx = ax; ax = bx; bx = cx;}
            if(ax != bx){return ax < bx ? -1 : 1;}
        }
    }
}

ejemplo de uso:

items.sort (getSortMethod ('- precio', '+ prioridad', '+ nombre'));

esto se ordenaría primero itemscon el más bajo price, y los vínculos con el elemento con el más alto priority. más lazos se rompen por el artículoname

donde elementos es una matriz como:

var items = [
    { name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
    { name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
    { name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
    { name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
    { name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 } ...
];

demostración en vivo: http://gregtaff.com/misc/multi_field_sort/

EDITAR: problema solucionado con Chrome.


Esto es brillante
Azure

¡Respuesta genial!
Marius

para mecanografiado (para no obtener un error TS2554: Expected 0 arguments, but got ..) use la sintaxis aquí: stackoverflow.com/a/4116634/5287221
Chananel P

6

Supongo que el operador ternario lo((aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;) tiene confundido. Debería consultar el enlace para comprenderlo mejor.

Hasta entonces, aquí está su código completo if / else.

grouperArray.sort(function (a, b) {
    if (a.gsize < b.gsize)
    {
        return -1;
    }
    else if (a.gsize > b.gsize)
    {
        return 1;
    }
    else
    {
        if (a.glow < b.glow)
        {
            return -1;
        }
        else if (a.glow > b.glow)
        {
            return 1;
        }
        return 0;
    }
});

6

Aquí hay una implementación para aquellos que deseen algo más genérico que funcione con cualquier número de campos.

Array.prototype.sortBy = function (propertyName, sortDirection) {

    var sortArguments = arguments;
    this.sort(function (objA, objB) {

        var result = 0;
        for (var argIndex = 0; argIndex < sortArguments.length && result === 0; argIndex += 2) {

            var propertyName = sortArguments[argIndex];
            result = (objA[propertyName] < objB[propertyName]) ? -1 : (objA[propertyName] > objB[propertyName]) ? 1 : 0;

            //Reverse if sort order is false (DESC)
            result *= !sortArguments[argIndex + 1] ? 1 : -1;
        }
        return result;
    });

}

Básicamente, puede especificar cualquier número de nombre de propiedad / dirección de clasificación:

var arr = [{
  LastName: "Doe",
  FirstName: "John",
  Age: 28
}, {
  LastName: "Doe",
  FirstName: "Jane",
  Age: 28
}, {
  LastName: "Foo",
  FirstName: "John",
  Age: 30
}];

arr.sortBy("LastName", true, "FirstName", true, "Age", false);
//Will return Jane Doe / John Doe / John Foo

arr.sortBy("Age", false, "LastName", true, "FirstName", false);
//Will return John Foo / John Doe / Jane Doe

3
grouperArray.sort(function (a, b) {
  var aSize = a.gsize;
  var bSize = b.gsize;
  var aLow = a.glow;
  var bLow = b.glow;
  console.log(aLow + " | " + bLow);      
  return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : ( (aLow < bLow ) ? -1 : (aLow > bLow ) ? 1 : 0 );
});

3
grouperArray.sort(function (a, b) {
     var aSize = a.gsize;     
     var bSize = b.gsize;     
     var aLow = a.glow;
     var bLow = b.glow;
     console.log(aLow + " | " + bLow);
     return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0); }); 

3

Aquí hay una implementación que usa la recursividad para ordenar por cualquier número de campos de ordenación desde 1 hasta infinito. Le pasa una matriz de resultados que es una matriz de objetos de resultado para ordenar, y una matriz de ordenaciones que es una matriz de objetos de ordenación que definen la ordenación. Cada objeto de clasificación debe tener una clave de "seleccionar" para el nombre de la clave por la que se ordena y una clave de "orden", que es una cadena que indica "ascendente" o "descendente".

sortMultiCompare = (a, b, sorts) => {
    let select = sorts[0].select
    let order = sorts[0].order
    if (a[select] < b[select]) {
        return order == 'ascending' ? -1 : 1
    } 
    if (a[select] > b[select]) {
        return order == 'ascending' ? 1 : -1
    }
    if(sorts.length > 1) {
        let remainingSorts = sorts.slice(1)
        return this.sortMultiCompare(a, b, remainingSorts)
    }
    return 0
}

sortResults = (results, sorts) => {
    return results.sort((a, b) => {
        return this.sortMultiCompare(a, b, sorts)
    })
}

// example inputs
const results = [
    {
        "LastName": "Doe",
        "FirstName": "John",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Doe",
        "FirstName": "Jane",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Johnson",
        "FirstName": "Kevin",
        "MiddleName": "Bill"
    }
]

const sorts = [
    {
        "select": "LastName",
        "order": "ascending"
    },
    {
        "select": "FirstName",
        "order": "ascending"
    },
    {
        "select": "MiddleName",
        "order": "ascending"
    }    
]

// call the function like this:
let sortedResults = sortResults(results, sorts)

2

Una forma dinámica de hacer eso con MÚLTIPLES teclas:

  • filtrar valores únicos de cada columna / clave de clasificación
  • poner en orden o invertirlo
  • agregar pesos de ancho zeropad para cada objeto según los valores de las teclas indexOf (valor)
  • ordenar usando pesos caclutados

ingrese la descripción de la imagen aquí

Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) { 
    sorts.map(sort => {            
        sort.uniques = Array.from(
            new Set(this.map(obj => obj[sort.key]))
        );

        sort.uniques = sort.uniques.sort((a, b) => {
            if (typeof a == 'string') {
                return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
            }
            else if (typeof a == 'number') {
                return sort.inverse ? (a < b) : (a > b ? 1 : 0);
            }
            else if (typeof a == 'boolean') {
                let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
                return x;
            }
            return 0;
        });
    });

    const weightOfObject = (obj) => {
        let weight = "";
        sorts.map(sort => {
            let zeropad = `${sort.uniques.length}`.length;
            weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
        });
        //obj.weight = weight; // if you need to see weights
        return weight;
    }

    this.sort((a, b) => {
        return weightOfObject(a).localeCompare( weightOfObject(b) );
    });

    return this;
}
});

Utilizar:

// works with string, number and boolean
let sortered = your_array.orderBy([
    {key: "type", inverse: false}, 
    {key: "title", inverse: false},
    {key: "spot", inverse: false},
    {key: "internal", inverse: true}
]);

ingrese la descripción de la imagen aquí


1

Esto es lo que yo uso

function sort(a, b) {
    var _a = "".concat(a.size, a.glow);
    var _b = "".concat(b.size, b.glow);
    return _a < _b;
}

Concat los dos elementos como una cadena y se ordenarán por un valor de cadena. Si lo desea, puede envolver _a y _b con parseInt para compararlos como números si sabe que serán numéricos.


1

Aquí está la solución para el caso, cuando tiene una clave de clasificación de prioridad, que podría no existir en algunos elementos en particular, por lo que debe ordenar por claves de reserva.

Un ejemplo de datos de entrada ( id2 es la clave de clasificación de prioridad):

const arr = [
    {id: 1},
    {id: 2, id2: 3},
    {id: 4},
    {id: 3},
    {id: 10, id2: 2},
    {id: 7},
    {id: 6, id2: 1},
    {id: 5},
    {id: 9, id2: 2},
    {id: 8},
];

Y la salida debería ser:

[ { id: 6, id2: 1 },
  { id: 9, id2: 2 },
  { id: 10, id2: 2 },
  { id: 2, id2: 3 },
  { id: 1 },
  { id: 3 },
  { id: 4 },
  { id: 5 },
  { id: 7 },
  { id: 8 } ]

La función del comparador será como:

arr.sort((a,b) => {
  if(a.id2 || b.id2) {
    if(a.id2 && b.id2) {
      if(a.id2 === b.id2) {
        return a.id - b.id;
      }
      return a.id2 - b.id2;
    }
    return a.id2 ? -1 : 1;
  }
  return a.id - b.id
});

PS En caso de que el .id de .id2 pueda ser ceros, considere usar typeof.


0
grouperArray.sort(
  function(a,b){return a.gsize == b.gsize ? a.glow - b.glow : a.gsize - b.gsize}
);

0
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    if (aSize !== aSize)
        return aSize - bSize;
    return a.glow - b.glow;
});

no probado, pero creo que debería funcionar.


0

En mi caso, ordeno la lista de notificaciones por parámetro 'importante' y por 'fecha'

  • paso 1: filtro las notificaciones por 'importante' y no importante

    let importantNotifications = notifications.filter(
            (notification) => notification.isImportant);
    
      let unImportantNotifications = notifications.filter(
            (notification) => !notification.isImportant);
    
  • paso 2: los ordeno por fecha

      sortByDate = (notifications) => {
      return notifications.sort((notificationOne, notificationTwo) => {
        return notificationOne.date - notificationTwo.date;
      });
    };
    
  • paso 3: fusionarlos

    [
        ...this.sortByDate(importantNotifications),
        ...this.sortByDate(unImportantNotifications),
      ];
    
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.