Pergunta

Tenho uma lista de objetos que desejo classificar com base em um campo attr do tipo string.Eu tentei usar -

list.sort(function (a, b) {
    return a.attr - b.attr
})

mas descobri que - parece não funcionar com strings em JavaScript.Como posso classificar uma lista de objetos com base em um atributo do tipo string?

Foi útil?

Solução

Usar String.prototype.localeCompare a pelo seu exemplo:

list.sort(function (a, b) {
    return ('' + a.attr).localeCompare(b.attr);
})

Forçamos a.attr a ser uma string para evitar exceções. localeCompare foi apoiado desde o Internet Explorer 6 e Firefox 1.Você também pode ver o seguinte código usado que não respeita uma localidade:

if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;

Outras dicas

Uma resposta atualizada (outubro de 2014)

Fiquei muito irritado com essa ordem de classificação natural das strings, então levei algum tempo para investigar esse problema.Eu espero que isso ajude.

Longa história curta

localeCompare() suporte de personagem é foda, basta usá-lo.Como apontado por Shog9, a resposta para sua pergunta é:

return item1.attr.localeCompare(item2.attr);

Bugs encontrados em todas as implementações de "ordem de classificação de string natural" de javascript personalizado

Existem várias implementações personalizadas por aí, tentando fazer comparação de strings com mais precisão, chamadas de "ordem de classificação natural de strings"

Ao "brincar" com essas implementações, sempre notei alguma escolha estranha de "ordem de classificação natural", ou melhor, erros (ou omissões na melhor das hipóteses).

Normalmente, os caracteres especiais (espaço, traço, e comercial, colchetes e assim por diante) não são processados ​​corretamente.

Você os encontrará aparecendo misturados em lugares diferentes, normalmente:

  • alguns estarão entre 'Z' maiúsculo e 'a' minúsculo
  • alguns estarão entre o '9' e o 'A' maiúsculo
  • alguns estarão depois de 'z' minúsculo

Quando seria de se esperar que todos os caracteres especiais fossem "agrupados" em um só lugar, exceto talvez o caractere especial de espaço (que seria sempre o primeiro caractere).Ou seja, tudo antes dos números, ou tudo entre números e letras (minúsculas e maiúsculas sendo "juntas" uma após a outra), ou tudo depois das letras.

Minha conclusão é que todos eles falham em fornecer uma ordem consistente quando começo a adicionar caracteres pouco incomuns (ou seja,caracteres com diacríticos ou caracteres como traço, ponto de exclamação e assim por diante).

Pesquisa sobre as implementações personalizadas:

Implementações nativas de "ordem de classificação de string natural" dos navegadores via localeCompare()

localeCompare() a implementação mais antiga (sem os argumentos locales e options) é suportada pelo IE6+, consulte http://msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx (role para baixo até o método localeCompare()).O embutido localeCompare() O método faz um trabalho muito melhor na classificação, até mesmo de caracteres internacionais e especiais.O único problema ao usar o localeCompare() método é esse "a localidade e a ordem de classificação usadas dependem inteiramente da implementação".Em outras palavras, ao usar localeCompare como stringOne.localeCompare(stringTwo):Firefox, Safari, Chrome e IE têm uma ordem de classificação diferente para Strings.

Pesquisa sobre as implementações nativas do navegador:

Dificuldade de "ordem de classificação natural de strings"

Implementando um algoritmo sólido (ou seja:consistente, mas também cobrindo uma ampla gama de personagens) é uma tarefa muito difícil.UTF8 contém mais de 2.000 caracteres & abrange mais de 120 scripts (idiomas).Por fim, existem algumas especificações para esta tarefa, é chamada de "Algoritmo de agrupamento Unicode", que pode ser encontrada em http://www.unicode.org/reports/tr10/ .Você pode encontrar mais informações sobre isso nesta pergunta que postei https://softwareengineering.stackexchange.com/questions/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

Conclusão final

Portanto, considerando o nível atual de suporte fornecido pelas implementações personalizadas de javascript que encontrei, provavelmente nunca veremos nada chegando perto de oferecer suporte a todos esses caracteres e scripts (linguagens).Portanto, prefiro usar o método localeCompare() nativo dos navegadores.Sim, tem a desvantagem de não ser consistente entre navegadores, mas os testes básicos mostram que cobre uma gama muito mais ampla de caracteres, permitindo ordens de classificação sólidas e significativas.

Então como apontado por Shog9, a resposta para sua pergunta é:

return item1.attr.localeCompare(item2.attr);

Leitura adicional:

Graças à boa resposta do Shog9, que me colocou na direção "certa", acredito

Resposta (em ECMAScript moderno)

list.sort((a, b) => (a.attr > b.attr) - (a.attr < b.attr))

Ou

list.sort((a, b) => +(a.attr > b.attr) || -(a.attr < b.attr))

Descrição

A conversão de um valor booleano para um número resulta no seguinte:

  • true -> 1
  • false -> 0

Considere três padrões possíveis:

  • x é maior que y: (x > y) - (y < x) -> 1 - 0 -> 1
  • x é igual a y: (x > y) - (y < x) -> 0 - 0 -> 0
  • x é menor que y: (x > y) - (y < x) -> 0 - 1 -> -1

(Alternativa)

  • x é maior que y: +(x > y) || -(x < y) -> 1 || 0 -> 1
  • x é igual a y: +(x > y) || -(x < y) -> 0 || 0 -> 0
  • x é menor que y: +(x > y) || -(x < y) -> 0 || -1 -> -1

Portanto, essas lógicas são equivalentes às funções típicas de comparação de classificação.

if (x == y) {
    return 0;
}
return x > y ? 1 : -1;

Você deve usar > ou < e == aqui.Então a solução seria:

list.sort(function(item1, item2) {
    var val1 = item1.attr,
        val2 = item2.attr;
    if (val1 == val2) return 0;
    if (val1 > val2) return 1;
    if (val1 < val2) return -1;
});

Eu estava preocupado com isso há muito tempo, então finalmente pesquisei isso e lhe dei uma razão prolixa de por que as coisas são como são.

De especificação:

Section 11.9.4   The Strict Equals Operator ( === )

The production EqualityExpression : EqualityExpression === RelationalExpression
is evaluated as follows: 
- Let lref be the result of evaluating EqualityExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating RelationalExpression.
- Let rval be GetValue(rref).
- Return the result of performing the strict equality comparison 
  rval === lval. (See 11.9.6)

Então agora vamos para 11.9.6

11.9.6   The Strict Equality Comparison Algorithm

The comparison x === y, where x and y are values, produces true or false. 
Such a comparison is performed as follows: 
- If Type(x) is different from Type(y), return false.
- If Type(x) is Undefined, return true.
- If Type(x) is Null, return true.
- If Type(x) is Number, then
...
- If Type(x) is String, then return true if x and y are exactly the 
  same sequence of characters (same length and same characters in 
  corresponding positions); otherwise, return false.

É isso. O operador triplo igual aplicado a strings retorna verdadeiro se os argumentos forem exatamente iguais (mesmo comprimento e mesmos caracteres nas posições correspondentes).

Então === funcionará nos casos em que estamos tentando comparar strings que podem ter chegado de fontes diferentes, mas que sabemos que eventualmente terão os mesmos valores - um cenário bastante comum para strings embutidas em nosso código.Por exemplo, se tivermos uma variável chamada connection_state, e desejamos saber qual dos seguintes estados ['connecting', 'connected', 'disconnecting', 'disconnected'] está agora, podemos usar diretamente o ===.

Mas há mais.Logo acima de 11.9.4, há uma breve nota:

NOTE 4     
  Comparison of Strings uses a simple equality test on sequences of code 
  unit values. There is no attempt to use the more complex, semantically oriented
  definitions of character or string equality and collating order defined in the 
  Unicode specification. Therefore Strings values that are canonically equal
  according to the Unicode standard could test as unequal. In effect this 
  algorithm assumes that both Strings are already in normalized form.

Hum.E agora?Strings obtidas externamente podem, e provavelmente serão, ser unicodey estranhas, e nosso gentil === não lhes fará justiça.Entra localeCompare para o resgate:

15.5.4.9   String.prototype.localeCompare (that)
    ...
    The actual return values are implementation-defined to permit implementers 
    to encode additional information in the value, but the function is required 
    to define a total ordering on all Strings and to return 0 when comparing
    Strings that are considered canonically equivalent by the Unicode standard. 

Podemos ir para casa agora.

dr;

Para comparar strings em javascript, use localeCompare;se você sabe que as strings não possuem componentes não-ASCII porque são, por exemplo, constantes internas do programa, então === também funciona.

Função de seta ternária aninhada

(a,b) => (a < b ? -1 : a > b ? 1 : 0)

Na sua operação na sua pergunta inicial, você está realizando a seguinte operação:

item1.attr - item2.attr

Então, supondo que sejam números (ou seja,item1.attr = "1", item2.attr = "2") Você ainda pode usar o operador "===" (ou outros avaliadores estritos), desde que garanta o tipo.O seguinte deve funcionar:

return parseInt(item1.attr) - parseInt(item2.attr);

Se forem alfanuméricos, use localCompare().

list.sort(function(item1, item2){
    return +(item1.attr > item2.attr) || +(item1.attr === item2.attr) - 1;
}) 

Como funcionam as amostras:

+('aaa'>'bbb')||+('aaa'==='bbb')-1
+(false)||+(false)-1
0||0-1
-1

+('bbb'>'aaa')||+('bbb'==='aaa')-1
+(true)||+(false)-1
1||0-1
1

+('aaa'>'aaa')||+('aaa'==='aaa')-1
+(false)||+(true)-1
0||1-1
0
<!doctype html>
<html>
<body>
<p id = "myString">zyxtspqnmdba</p>
<p id = "orderedString"></p>
<script>
var myString = document.getElementById("myString").innerHTML;
orderString(myString);
function orderString(str) {
    var i = 0;
    var myArray = str.split("");
    while (i < str.length){
        var j = i + 1;
        while (j < str.length) {
            if (myArray[j] < myArray[i]){
                var temp = myArray[i];
                myArray[i] = myArray[j];
                myArray[j] = temp;
            }
            j++;
        }
        i++;
    }
    var newString = myArray.join("");
    document.getElementById("orderedString").innerHTML = newString;
}
</script>
</body>
</html>
var str = ['v','a','da','c','k','l']
var b = str.join('').split('').sort().reverse().join('')
console.log(b)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top