Pregunta ¿Cómo ordenar una matriz asociativa por sus valores en Javascript?


Tengo la matriz asociativa:

array["sub2"] = 1;
array["sub0"] = -1;
array["sub1"] = 0;
array["sub3"] = 1;
array["sub4"] = 0;

¿Cuál es la forma más elegante de ordenar (descender) por sus valores donde el resultado sería una matriz con los índices respectivos en este orden?

sub2, sub3, sub1, sub4, sub0?

75
2018-03-04 22:17


origen


Respuestas:


Javascript no tiene "matrices asociativas" en la forma en que piensas en ellas. En cambio, simplemente tiene la capacidad de establecer propiedades de objetos usando una sintaxis similar a una matriz (como en su ejemplo), además de la capacidad de iterar sobre las propiedades de un objeto.

El resultado de esto es que no hay garantía en cuanto a orden en el que iteras sobre las propiedades, por lo que no hay nada como un género para ellas. En su lugar, deberá convertir las propiedades de su objeto en una matriz "verdadera" (que garantiza el orden). Aquí hay un fragmento de código para convertir un objeto en una matriz de dos tuplas (matrices de dos elementos), ordenándolo como usted describe, y luego iterando sobre él:

var tuples = [];

for (var key in obj) tuples.push([key, obj[key]]);

tuples.sort(function(a, b) {
    a = a[1];
    b = b[1];

    return a < b ? -1 : (a > b ? 1 : 0);
});

for (var i = 0; i < tuples.length; i++) {
    var key = tuples[i][0];
    var value = tuples[i][1];

    // do something with key and value
}

Puede que le resulte más natural ajustar esto en una función que toma una devolución de llamada:

function bySortedValue(obj, callback, context) {
  var tuples = [];

  for (var key in obj) tuples.push([key, obj[key]]);

  tuples.sort(function(a, b) {
    return a[1] < b[1] ? 1 : a[1] > b[1] ? -1 : 0
  });

  var length = tuples.length;
  while (length--) callback.call(context, tuples[length][0], tuples[length][1]);
}

bySortedValue({
  foo: 1,
  bar: 7,
  baz: 3
}, function(key, value) {
  document.getElementById('res').innerHTML += `${key}: ${value}<br>`
});
<p id='res'>Result:<br/><br/><p>


108
2018-03-04 22:31



En lugar de corregirte sobre la semántica de una "matriz asociativa", creo que esto es lo que quieres:

function getSortedKeys(obj) {
    var keys = []; for(var key in obj) keys.push(key);
    return keys.sort(function(a,b){return obj[b]-obj[a]});
}

Usted coloca un objeto (como el suyo) y obtiene una matriz de las propiedades - eh properties - atrás, ordenadas descendiendo por el valor (numérico) de los valores, eh, del objeto eh.

Esto solo funciona si tus valores son numéricos. Tweek el pequeño function(a,b) allí para cambiar el mecanismo de clasificación para que trabaje de forma ascendente, o trabaje para string valores (por ejemplo). Izquierda como ejercicio para el lector.

EDITAR: la gente sigue votando esta respuesta, pero es muy vieja. Por favor   reconsiderar por qué no solo estás usando Object.keys () hoy en día.


69
2017-08-04 20:25



Discusión continua y otras soluciones cubiertas en ¿Cómo ordenar una matriz (asociativa) por valor? con la mejor solución (para mi caso) por saml (citado a continuación).

Las matrices solo pueden tener índices numéricos. Debería volver a escribir esto como un Objeto o como una Matriz de Objetos.

var status = new Array();
status.push({name: 'BOB', val: 10});
status.push({name: 'TOM', val: 3});
status.push({name: 'ROB', val: 22});
status.push({name: 'JON', val: 7});

Si te gusta el status.push método, puede ordenarlo con:

status.sort(function(a,b) {
    return a.val - b.val;
});

15
2017-10-08 23:27



Realmente no hay nada como una "matriz asociativa" en JavaScript. Lo que tienes allí es solo un viejo objeto simple. Por supuesto, funcionan como arreglos asociativos tipo kind-of, y las claves están disponibles, pero no hay semántica en el orden de las teclas.

Puede convertir su objeto en una matriz de objetos (pares clave / valor) y ordenarlo:

function sortObj(object, sortFunc) {
  var rv = [];
  for (var k in object) {
    if (object.hasOwnProperty(k)) rv.push({key: k, value:  object[k]});
  }
  rv.sort(function(o1, o2) {
    return sortFunc(o1.key, o2.key);
  });
  return rv;
}

Entonces llamarías eso con una función de comparación.


5
2018-03-04 22:30



Aquí hay una variación de la respuesta de Ben Blank, si no te gustan las tuplas.

Esto te ahorra algunos personajes.

var keys = [];
for (var key in sortme) {
  keys.push(key);
}

keys.sort(function(k0, k1) {
  var a = sortme[k0];
  var b = sortme[k1];
  return a < b ? -1 : (a > b ? 1 : 0);
});

for (var i = 0; i < keys.length; ++i) {
  var key = keys[i];
  var value = sortme[key];
  // Do something with key and value.
}

4
2018-05-02 18:21



No se requiere complicación innecesaria ...

function sortMapByValue(map)
{
    var tupleArray = [];
    for (var key in map) tupleArray.push([key, map[key]]);
    tupleArray.sort(function (a, b) { return a[1] - b[1] });
    return tupleArray;
}

4
2018-01-10 05:07



Yo uso $ .each de jquery pero puedes hacerlo con un ciclo for, una mejora es esta:

        //.ArraySort(array)
        /* Sort an array
         */
        ArraySort = function(array, sortFunc){
              var tmp = [];
              var aSorted=[];
              var oSorted={};

              for (var k in array) {
                if (array.hasOwnProperty(k)) 
                    tmp.push({key: k, value:  array[k]});
              }

              tmp.sort(function(o1, o2) {
                    return sortFunc(o1.value, o2.value);
              });                     

              if(Object.prototype.toString.call(array) === '[object Array]'){
                  $.each(tmp, function(index, value){
                      aSorted.push(value.value);
                  });
                  return aSorted;                     
              }

              if(Object.prototype.toString.call(array) === '[object Object]'){
                  $.each(tmp, function(index, value){
                      oSorted[value.key]=value.value;
                  });                     
                  return oSorted;
              }               
     };

Entonces ahora puedes hacer

    console.log("ArraySort");
    var arr1 = [4,3,6,1,2,8,5,9,9];
    var arr2 = {'a':4, 'b':3, 'c':6, 'd':1, 'e':2, 'f':8, 'g':5, 'h':9};
    var arr3 = {a: 'green', b: 'brown', c: 'blue', d: 'red'};
    var result1 = ArraySort(arr1, function(a,b){return a-b});
    var result2 = ArraySort(arr2, function(a,b){return a-b});
    var result3 = ArraySort(arr3, function(a,b){return a>b});
    console.log(result1);
    console.log(result2);       
    console.log(result3);

1
2017-07-19 13:22



Simplemente está ahí y alguien está buscando tuplas basadas en géneros. Esto comparará el primer elemento del objeto en la matriz, que el segundo elemento y así sucesivamente. es decir, en el ejemplo siguiente, se comparará primero con "a", luego con "b" y así sucesivamente.

let arr = [
    {a:1, b:2, c:3},
    {a:3, b:5, c:1},
    {a:2, b:3, c:9},
    {a:2, b:5, c:9},
    {a:2, b:3, c:10}    
]

function getSortedScore(obj) {
    var keys = []; 
    for(var key in obj[0]) keys.push(key);
    return obj.sort(function(a,b){
        for (var i in keys) {
            let k = keys[i];
            if (a[k]-b[k] > 0) return -1;
            else if (a[k]-b[k] < 0) return 1;
            else continue;
        };
    });
}

console.log(getSortedScore(arr))

OUPUTS

 [ { a: 3, b: 5, c: 1 },
  { a: 2, b: 5, c: 9 },
  { a: 2, b: 3, c: 10 },
  { a: 2, b: 3, c: 9 },
  { a: 1, b: 2, c: 3 } ]

0
2018-06-27 10:45



El mejor enfoque para el caso específico aquí, en mi opinión, es el commonpike sugirió. Una pequeña mejora que sugeriría que funciona en los navegadores modernos es:

// aao is the "associative array" you need to "sort"
Object.keys(aao).sort(function(a,b){return aao[b]-aao[a]});

Esto podría aplicarse fácilmente y funcionar bien en el caso específico aquí para que pueda hacer:

let aoo={};
aao["sub2"]=1;
aao["sub0"]=-1;
aao["sub1"]=0;
aao["sub3"]=1;
aao["sub4"]=0;

let sk=Object.keys(aao).sort(function(a,b){return aao[b]-aao[a]});

// now you can loop using the sorted keys in `sk` to do stuffs
for (let i=sk.length-1;i>=0;--i){
 // do something with sk[i] or aoo[sk[i]]
}

Además de esto, proporciono aquí una función más "genérica" ​​que puede usar para clasificar incluso en un rango más amplio de situaciones y que combina la mejora que acabo de sugerir con los enfoques de las respuestas de Ben Blank (ordenando también valores de cadena) y PopeJohnPaulII (ordenando por campo / propiedad de objeto específico) y le permite decidir si desea un orden ascendente o descendente, aquí está:

// aao := is the "associative array" you need to "sort"
// comp := is the "field" you want to compare or "" if you have no "fields" and simply need to compare values
// intVal := must be false if you need comparing non-integer values
// desc := set to true will sort keys in descendant order (default sort order is ascendant)
function sortedKeys(aao,comp="",intVal=false,desc=false){
  let keys=Object.keys(aao);
  if (comp!="") {
    if (intVal) {
      if (desc) return keys.sort(function(a,b){return aao[b][comp]-aao[a][comp]});
      else return keys.sort(function(a,b){return aao[a][comp]-aao[a][comp]});
    } else {
      if (desc) return keys.sort(function(a,b){return aao[b][comp]<aao[a][comp]?1:aao[b][comp]>aao[a][comp]?-1:0});
      else return keys.sort(function(a,b){return aao[a][comp]<aao[b][comp]?1:aao[a][comp]>aao[b][comp]?-1:0});
    }
  } else {
    if (intVal) {
      if (desc) return keys.sort(function(a,b){return aao[b]-aao[a]});
      else return keys.sort(function(a,b){return aao[a]-aao[b]});
    } else {
      if (desc) return keys.sort(function(a,b){return aao[b]<aao[a]?1:aao[b]>aao[a]?-1:0});
      else return keys.sort(function(a,b){return aao[a]<aao[b]?1:aao[a]>aao[b]?-1:0});
    }
  }
}

Puede probar las funcionalidades intentando algo así como el siguiente código:

let items={};
items['Edward']=21;
items['Sharpe']=37;
items['And']=45;
items['The']=-12;
items['Magnetic']=13;
items['Zeros']=37;
//equivalent to:
//let items={"Edward": 21, "Sharpe": 37, "And": 45, "The": -12, ...};

console.log("1: "+sortedKeys(items));
console.log("2: "+sortedKeys(items,"",false,true));
console.log("3: "+sortedKeys(items,"",true,false));
console.log("4: "+sortedKeys(items,"",true,true));
/* OUTPUT
1: And,Sharpe,Zeros,Edward,Magnetic,The
2: The,Magnetic,Edward,Sharpe,Zeros,And
3: The,Magnetic,Edward,Sharpe,Zeros,And
4: And,Sharpe,Zeros,Edward,Magnetic,The
*/

items={};
items['k1']={name:'Edward',value:21};
items['k2']={name:'Sharpe',value:37};
items['k3']={name:'And',value:45};
items['k4']={name:'The',value:-12};
items['k5']={name:'Magnetic',value:13};
items['k6']={name:'Zeros',value:37};

console.log("1: "+sortedKeys(items,"name"));
console.log("2: "+sortedKeys(items,"name",false,true));
/* OUTPUT
1: k6,k4,k2,k5,k1,k3
2: k3,k1,k5,k2,k4,k6
*/

Como ya he dicho, puede recorrer las teclas ordenadas si necesita hacer cosas

let sk=sortedKeys(aoo);
// now you can loop using the sorted keys in `sk` to do stuffs
for (let i=sk.length-1;i>=0;--i){
 // do something with sk[i] or aoo[sk[i]]
}

Por último, pero no menos importante, algunas referencias útiles a Object.keys y Array.sort


0
2018-06-30 16:20