Classificar matriz por nome (alfabeticamente) em Javascript

646

Eu tenho uma matriz (veja abaixo para um objeto na matriz) que eu preciso classificar por nome usando JavaScript. Como eu posso fazer isso?

var user = {
   bio: null,
   email:  "[email protected]",
   firstname: "Anna",
   id: 318,
   lastAvatar: null,
   lastMessage: null,
   lastname: "Nickson",
   nickname: "anny"
};
Jonathan Clark
fonte
possível duplicata de Ordenar JavaScript array de objetos
Felix Kling

Respostas:

1067

Suponha que você tenha uma matriz users. Você pode usar users.sorte passar uma função que recebe dois argumentos e os compara (comparador)

Deve retornar

  • algo negativo se o primeiro argumento for menor que o segundo (deve ser colocado antes do segundo na matriz resultante)
  • algo positivo se o primeiro argumento for maior (deve ser colocado após o segundo)
  • 0 se esses dois elementos forem iguais.

No nosso caso, se dois elementos existirem ae bqueremos comparar a.firstnameeb.firstname

Exemplo:

users.sort(function(a, b){
    if(a.firstname < b.firstname) { return -1; }
    if(a.firstname > b.firstname) { return 1; }
    return 0;
})

Este código funcionará com qualquer tipo.

Observe que, na "vida real" ™, você frequentemente deseja ignorar maiúsculas e minúsculas, classificar corretamente sinais diacríticos, símbolos estranhos como ß etc. quando comparar seqüências de caracteres, portanto, pode usar localeCompare. Veja outras respostas para maior clareza.

RiaD
fonte
5
Friggin awesome ... Estou classificando um nodelist por id ... funciona como um encanto. Thx a tonelada!
Cody
74
Para quem chega mais tarde, a resposta de Mrchief é melhor porque não faz distinção entre maiúsculas e minúsculas.
Mlienau #
25
@mlienau, eu não diria que é melhor ou pior. É apenas mais uma coisa
RiaD
19
@RiaD justo o suficiente. Apenas não consigo pensar em muitos casos de classificação alfabética de itens, incluindo maiúsculas e minúsculas, onde 'Zebra' aparece na lista antes de 'maçã' seria muito útil.
mlienau
15
Este código funcionará apenas em países de língua inglesa. Em outros países, você deve usar a resposta da ovunccetinalocaleCompare .
Spoike
545

O menor código possível com o ES6!

users.sort((a, b) => a.firstname.localeCompare(b.firstname))

O suporte básico String.prototype.localeCompare () é universal!

Nachiketha
fonte
27
De longe a melhor resposta se você está vivendo em 2017
nathanbirrell
52
Melhor resposta também se você mora em 2018;)
Simone
49
Provavelmente também será a melhor resposta para 2019.
Ahmad Maleki
23
2020 talvez? ou naaa?
precisa saber é o seguinte
15
ah cara, em 2030 a terra inunda e isso só funciona nas montanhas.
Starfs
343

Algo assim:

array.sort(function(a, b){
 var nameA=a.name.toLowerCase(), nameB=b.name.toLowerCase();
 if (nameA < nameB) //sort string ascending
  return -1;
 if (nameA > nameB)
  return 1;
 return 0; //default return value (no sorting)
});
Mrchief
fonte
43
Ao classificar as strings 'toLowerCase ()' é muito importante - as letras maiúsculas podem afetar sua classificação.
MarzSocks
3
Caso alguém mais esteja se perguntando o que o toLowerCase afeta, não é muito:'a'>'A' //true 'z'>'a' //true 'A'>'z' //false
SimplGy
14
@SimplGy Eu diria que seu impacto é um pouco mais do que você está dando crédito. Por exemplo, conforme declarado nos comentários da resposta aceita, é importante saber se sua função de classificação classificará ou não a string 'Zebra'mais alta que a string 'apple', o que será feito se você não usar o .toLowerCase().
PrinceTyke
1
@PrinceTyke, sim, esse é um bom caso. 'Z' < 'a' // truee'z' < 'a' // false
SimplGy 5/08/2015
2
Todos os pontos válidos e eu diria que a classificação é sempre um tipo de coisa "depende". Se você estiver classificando por nomes, a distinção entre maiúsculas e minúsculas pode não importar. Em outros casos, pode ser. Independentemente disso, acho que adotar a situação específica não é difícil depois que você obtém a ideia central.
Mrchief 5/08/2015
336

Se as cadeias comparadas contiverem caracteres unicode, você poderá usar a localeComparefunção de Stringclasse da seguinte maneira:

users.sort(function(a,b){
    return a.firstname.localeCompare(b.firstname);
})
ovunccetina
fonte
3
String.localeCompare não é compatível com o Safari e IE <11 :)
CORSAIR
13
@ CORSAIR é suportado, é apenas o segundo e o terceiro parâmetro que não são suportados.
Codler
6
@CORSAIR, o uso no exemplo é suportado em todos os principais navegadores IE: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . O LocaleCompare também leva em consideração a capitalização, por isso acho que essa implementação deve ser considerada a melhor prática.
Matt Jensen
9
users.sort ((a, b) => a.firstname.localeCompare (b.firstname)) // Versão curta e doce do ES6!
precisa saber é o seguinte
4
tanques mano. no uso do ES6users.sort((a, b) => a.name.localeCompare(b.name))
Mohmmad Ebrahimi Aval 11/10
32

Nice little ES6 one liner:

users.sort((a, b) => a.firstname !== b.firstname ? a.firstname < b.firstname ? -1 : 1 : 0);
Sam Logan
fonte
2
Este está incompleto, pois não lida com valores iguais corretamente.
Karl-Johan Sjögren
1
Claro, obrigado por manchas que, eu adicionei um cheque de valores correspondentes
Sam Logan
não é mau Eu acho que se você realmente quer tudo em uma linha, ternários aninhados ficar confusa para facilitar a leitura embora
russter
24

Podemos usar o localeCompare, mas também precisamos verificar as chaves quanto aos valores de falsey

O código abaixo não funcionará se uma entrada estiver com lname ausente.

obj.sort((a, b) => a.lname.localeCompare(b.lname))

Então, precisamos verificar o valor de Falsey como abaixo

let obj=[
{name:'john',lname:'doe',address:'Alaska'},
{name:'tom',lname:'hopes',address:'California'},
{name:'harry',address:'Texas'}
]
let field='lname';
console.log(obj.sort((a, b) => (a[field] || "").toString().localeCompare((b[field] || "").toString())));

OU

podemos usar lodash, é muito simples. Ele detectará os valores retornados, ou seja, número ou sequência e fará a classificação de acordo.

import sortBy from 'lodash/sortBy';
sortBy(obj,'name')

https://lodash.com/docs/4.17.5#sortBy

sumit
fonte
2
Lodash é bem arrumado! Não tinha ouvido falar disso antes. Funciona bem!
Derk Jan Speelman
15

O underscorejs oferece a muito boa função _.sortBy:

_.sortBy([{a:1},{a:3},{a:2}], "a")

ou você pode usar uma função de classificação personalizada:

_.sortBy([{a:"b"},{a:"c"},{a:"a"}], function(i) {return i.a.toLowerCase()})
Peter T.
fonte
2
o segundo exemplo envolve retornar strings. Será que sortBydetectar os valores devolvidos são cadeias e, portanto, realiza uma classificação alfabética? Obrigado
superjos
12

Em caso estamos classificando nomes ou algo com caracteres especiais, como ñ ou AEIOU (commons em espanhol) nós poderíamos usar o params locales ( es para o espanhol, neste caso) e opções como este:

let user = [{'firstname': 'Az'},{'firstname': 'Áb'},{'firstname':'ay'},{'firstname': 'Ña'},{'firstname': 'Nz'},{'firstname': 'ny'}];


user.sort((a, b) => a.firstname.localeCompare(b.firstname, 'es', {sensitivity: 'base'}))


console.log(user)

As opções oficiais de localidade podem ser encontradas aqui em iana , es (espanhol), de (alemão), fr (francês). Sobre a sensibilidade base significa:

Somente cadeias que diferem em letras base são comparadas como desiguais. Exemplos: a ≠ b, a = á, a = A.

Emeeus
fonte
8

Basicamente, você pode classificar matrizes com o método sort, mas se você quiser classificar objetos, precisará passar a função para classificar o método do array, portanto, darei um exemplo usando seu array

user = [{
bio: "<null>",
email: "[email protected]",
firstname: 'Anna',
id: 318,
"last_avatar": "<null>",
"last_message": "<null>",
lastname: 'Nickson',
nickname: 'anny'
},
{
bio: "<null>",
email: "[email protected]",
firstname: 'Senad',
id: 318,
"last_avatar": "<null>",
"last_message": "<null>",
lastname: 'Nickson',
nickname: 'anny'
},
{
bio: "<null>",
email: "[email protected]",
firstname: 'Muhamed',
id: 318,
"last_avatar": "<null>",
"last_message": "<null>",
lastname: 'Nickson',
nickname: 'anny'
}];

var ar = user.sort(function(a, b)
{
  var nA = a.firstname.toLowerCase();
  var nB = b.firstname.toLowerCase();

  if(nA < nB)
    return -1;
  else if(nA > nB)
    return 1;
 return 0;
});
Senad Meškin
fonte
por que retornar 0 no final?
Nobita
=, Quando as cordas são mesmo
Senad Meskin
mas nesse caso não deve elseser removido? Caso contrário, isso return 0nunca será lido
Nobita
2
que não é "coisa", é "else if"
Senad Meskin
8

Inspirado nesta resposta,

users.sort((a,b) => (a.firstname  - b.firstname));
Isuru Siriwardana
fonte
8

Uma notação mais compacta:

user.sort(function(a, b){
    return a.firstname === b.firstname ? 0 : a.firstname < b.firstname ? -1 : 1;
})
James Andrews
fonte
é mais compacto, mas viola seu contrato: sign(f(a, b)) =-sign(f(b, a))(para a = b)
Riad
3
return a.firstname == b.firstnamedeve usar ===para completude
puiu
6

também para classificação asec e desc, você pode usar isso: suponha que tenhamos uma variável SortType que especifica a classificação crescente ou a classificação descendente desejada:

 users.sort(function(a,b){
            return   sortType==="asc"? a.firstName.localeCompare( b.firstName): -( a.firstName.localeCompare(  b.firstName));
        })
ghazaleh javaheri
fonte
5

Uma função generalizada pode ser escrita como abaixo

    function getSortedData(data, prop, isAsc) {
        return data.sort((a, b) => (a[prop] < b[prop] ? -1 : 1) * (isAsc ? 1 : -1));
   }

você pode passar os parâmetros abaixo

  1. Os dados que você deseja classificar
  2. A propriedade nos dados deve ser classificada
  3. O último parâmetro é do tipo booleano. Ele verifica se você deseja classificar por ascendente ou descendente
Santosh
fonte
4

tentar

users.sort((a,b)=> (a.firstname>b.firstname)*2-1)

Kamil Kiełczewski
fonte
2
a função de classificação deve retornar -1,0,1. este retorna apenas -1,1.
Radu Luncasu
@RaduLuncasu - você pode fornecer dados de teste (matriz de usuários) que mostram que a solução acima dá resultado errado? (até onde eu sei, a falta de zero não é problema) #
Kamil Kiełczewski 26/06/19
Se compareFunction (a, b) retornar 0, deixe aeb inalterados um com o outro, mas classificados com relação a todos os elementos diferentes.
Radu Luncasu
@RaduLuncasu ok, mas essas informações não significam que zero é obrigatório. A melhor maneira de mostrar que é preparar os dados que não funciona - mas tanto eu sei que isso é impossível - por exemplo[9,8,7,6,5,1,2,3].sort((a,b) => a<b ? -1 : 1)
Kamil Kiełczewski
3

Você pode usar isso para objetos

transform(array: any[], field: string): any[] {
return array.sort((a, b) => a[field].toLowerCase() !== b[field].toLowerCase() ? a[field].toLowerCase() < b[field].toLowerCase() ? -1 : 1 : 0);}
Andre Coetzee
fonte
2

em palavras simples, você pode usar esse método

users.sort(function(a,b){return a.firstname < b.firstname ? -1 : 1});
FarukT
fonte
1

Você pode usar o método de matriz incorporado - sort. Esse método usa um método de retorno de chamada como um parâmetro



    // custom sort function to be passed as param/callback to the Array's sort method
    function myCustomSort(a, b) {
        return (a.toLowerCase() > b.toLowerCase()) ? 1 : -1;
    }

    // Actual method to be called by entity that needs sorting feature
    function sortStrings() {
        var op = Array.prototype.sort.call(arguments, myCustomSort);
    }

    // Testing the implementation
    var sortedArray = sortStrings("Burger", "Mayo1", "Pizza", "boxes", "Apples", "Mayo");
    console.log(sortedArray); //["Apples", "boxes", "Burger", "Mayo", "Mayo1", "Pizza"]


Pontos principais a serem observados para a compreensão deste código.

  1. O método personalizado, nesse caso, myCustomSortdeve retornar +1 ou -1 para cada comparação de pares de elementos (da matriz de entrada).
  2. Use toLowerCase()/ toUpperCase()no método de retorno de chamada de classificação personalizado, para que a diferença entre maiúsculas e minúsculas não afete a correção do processo de classificação.

Espero que esta seja uma explicação suficientemente clara. Sinta-se livre para comentar se você acha que são necessárias mais informações.

Felicidades!

shanky
fonte
Não é uma resposta adequada porque executa apenas menos e maior que verificações e não a verificação igual.
Steel Brain
também não traz nada de novo para a mesa, em comparação com qualquer uma das respostas que estão lá desde ~ 2011.
Amenthes
1

Introduziu as principais respostas em um protótipo para classificar por chave.

Array.prototype.alphaSortByKey= function (key) {
    this.sort(function (a, b) {
        if (a[key] < b[key])
            return -1;
        if (a[key] > b[key])
            return 1;
        return 0;
    });
    return this;
};
msponagle
fonte
0

Você pode usar algo semelhante, para se livrar da distinção entre maiúsculas e minúsculas

users.sort(function(a, b){

  //compare two values
  if(a.firstname.toLowerCase() < b.firstname.toLowerCase()) return -1;
  if(a.firstname.toLowerCase() > b.firstname.toLowerCase()) return 1;
  return 0;

})
SAN_WEB
fonte
7
Mesma resposta que a de Mrchief. Com a desvantagem adicional de que o toLowerCase é feito quatro vezes por comparação, em vez de duas vezes.
Amenthes
0

Minha implementação, funciona muito bem em versões mais antigas do ES:

sortObject = function(data) {
    var keys = Object.keys(data);
    var result = {};

    keys.sort();

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

        result[key] = data[key];
    }

    return result;
};
extremo
fonte
0

Apenas para o registro, se você deseja ter uma função de classificação nomeada, a sintaxe é a seguinte:

let sortFunction = (a, b) => {
 if(a.firstname < b.firstname) { return -1; }
 if(a.firstname > b.firstname) { return 1; }
 return 0;
})
users.sort(sortFunction)

Observe que o seguinte NÃO funciona:

users.sort(sortFunction(a,b))
Katinka Hesselink
fonte