Eu tenho a matriz associativa:
array["sub2"] = 1;
array["sub0"] = -1;
array["sub1"] = 0;
array["sub3"] = 1;
array["sub4"] = 0;
Qual é a maneira mais elegante de classificar (decrescente) por seus valores, onde o resultado seria uma matriz com os respectivos índices nesta ordem:
sub2, sub3, sub1, sub4, sub0
javascript
arrays
sorting
associative
John smith
fonte
fonte
Respostas:
Javascript não tem "matrizes associativas" da maneira como você as considera. Em vez disso, você simplesmente tem a capacidade de definir as propriedades do objeto usando a sintaxe do tipo array (como no seu exemplo), além da capacidade de iterar nas propriedades de um objeto.
O resultado disso é que não há garantia quanto à ordem em que você itera nas propriedades, portanto, não há nada como uma classificação para elas. Em vez disso, você precisará converter as propriedades do objeto em uma matriz "verdadeira" (que garante a ordem). Aqui está um snippet de código para converter um objeto em uma matriz de duas tuplas (matrizes de dois elementos), classificando-o conforme você descreve e, em seguida, iterando sobre ele:
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 }
Você pode achar mais natural envolver isso em uma função que receba um retorno de chamada:
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>
fonte
Em vez de corrigi-lo sobre a semântica de uma 'matriz associativa', acho que é isso que você quer:
function getSortedKeys(obj) { var keys = keys = Object.keys(obj); return keys.sort(function(a,b){return obj[b]-obj[a]}); }
para navegadores muito antigos , use este:
function getSortedKeys(obj) { var keys = []; for(var key in obj) keys.push(key); return keys.sort(function(a,b){return obj[b]-obj[a]}); }
Você despeja em um objeto (como o seu) e obtém uma matriz das chaves - eh propriedades - de volta, classificada em ordem decrescente pelo valor (numérico) dos, eh, valores do, eh, objeto.
Isso só funciona se seus valores forem numéricos. Tweek o pouco
function(a,b)
lá dentro para mudar o mecanismo de classificação para trabalhar de forma ascendente ou trabalhar porstring
valores (por exemplo). Deixado como um exercício para o leitor.fonte
Discussão contínua e outras soluções abordadas em Como classificar uma matriz (associativa) por valor? com a melhor solução (para o meu caso) sendo por saml (citado abaixo).
Matrizes podem ter apenas índices numéricos. Você precisaria reescrever isso como um objeto ou uma 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});
Se gostar do
status.push
método, você pode classificá-lo com:status.sort(function(a,b) { return a.val - b.val; });
fonte
sort()
trata de maneira diferente. Isso me confundiu por uma hora até que encontrei este stackoverflow.com/questions/6712034/… Exemplo;a.name.toLowerCase() > b.name.toLowerCase()
Realmente não existe algo como uma "matriz associativa" em JavaScript. O que você tem aí é apenas um objeto simples e antigo. Eles funcionam como arrays associativos, é claro, e as chaves estão disponíveis, mas não há semântica em torno da ordem das chaves.
Você pode transformar seu objeto em uma matriz de objetos (pares de chave / valor) e classificá-lo:
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; }
Então você chamaria isso com uma função de comparador.
fonte
Aqui está uma variação da resposta de Ben blank, se você não gosta de tuplas.
Isso economiza alguns caracteres.
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. }
fonte
Nenhuma complicação desnecessária necessária ...
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; }
fonte
var stuff = {"a":1 , "b":3 , "c":0 } sortMapByValue(stuff) [Array[2], Array[2], Array[2]]
eu uso $ .each do jquery, mas você pode fazer isso com um loop for, uma melhoria é 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; } };
Então agora você pode fazer
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);
fonte
A melhor abordagem para o caso específico aqui, em minha opinião, é aquela sugerida pelo commonpike . Uma pequena melhoria que eu sugiro que funciona em navegadores modernos é:
// aao is the "associative array" you need to "sort" Object.keys(aao).sort(function(a,b){return aao[b]-aao[a]});
Isso pode se aplicar facilmente e funcionar muito bem no caso específico aqui, para que você possa fazer:
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]] }
Além disso, apresento aqui uma função mais "genérica" que você pode usar para classificar até mesmo em uma gama mais ampla de situações e que mistura a melhoria que acabei de sugerir com as abordagens das respostas de Ben Blank (classificando também valores de string) e PopeJohnPaulII ( classificação por campo / propriedade de objeto específico) e permite que você decida se deseja uma ordem ascendente ou descendente, aqui 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}); } } }
Você pode testar as funcionalidades tentando algo como o seguinte 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 eu já disse, você pode fazer um loop sobre as chaves classificadas se precisar fazer outras coisas
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, mas não menos importante, algumas referências úteis a Object.keys e Array.sort
fonte
Só para que ele esteja lá fora e alguém esteja procurando por classificações baseadas em tupla. Isso irá comparar o primeiro elemento do objeto na matriz, do que o segundo elemento e assim por diante. ou seja, no exemplo abaixo, ele irá comparar primeiro por "a", depois por "b" e assim por diante.
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 } ]
fonte
A resposta de @commonpike é "a certa", mas à medida que ele comenta ...
Sim ..
Object.keys()
é muito melhor .Mas o que é ainda melhor ? Duh, é isso aí
coffeescript
!sortedKeys = (x) -> Object.keys(x).sort (a,b) -> x[a] - x[b] sortedKeys 'a' : 1 'b' : 3 'c' : 4 'd' : -1
fonte