Como classificar a matriz bidimensional por valor de coluna?

90

Alguém pode me ajudar a classificar um Array 2 dimensional em JavaScript?

Terá dados no seguinte formato:

[12, AAA]
[58, BBB]
[28, CCC]
[18, DDD]

Deve ficar assim quando classificado:

[12, AAA]
[18, DDD]
[28, CCC]
[58, BBB]

Então, basicamente, classificando pela primeira coluna.

Felicidades

Alex
fonte
3
Aqui está tudo que você precisa saber: MDN - Array.sort ()
jahroy
1
por favor, aceite a resposta de @PramodVemulapalli, todos aqueles que votaram alto estão errados!
Bergi
@jahroy: Não se trata da coerção de tipo, mas dos requisitos para funções de comparação consistentes.
Bergi

Respostas:

113

É simples assim:

var a = [[12, 'AAA'], [58, 'BBB'], [28, 'CCC'],[18, 'DDD']];

a.sort(sortFunction);

function sortFunction(a, b) {
    if (a[0] === b[0]) {
        return 0;
    }
    else {
        return (a[0] < b[0]) ? -1 : 1;
    }
}

Eu convido você a ler a documentação .

Se quiser classificar pela segunda coluna, você pode fazer o seguinte:

a.sort(compareSecondColumn);

function compareSecondColumn(a, b) {
    if (a[1] === b[1]) {
        return 0;
    }
    else {
        return (a[1] < b[1]) ? -1 : 1;
    }
}
jahroy
fonte
4
Por favor, teste seu código. jsfiddle.net/DuR4B/2 . Direto do link de documentação que você postou: "Se compareFunction não for fornecido, os elementos são classificados convertendo-os em strings e comparando-os em ordem lexicográfica (" dicionário "ou" lista telefônica ", não numérica). Por exemplo," 80 "vem antes de "9" em ordem lexicográfica, mas em um tipo numérico, 9 vem antes de 80. "
Ian
3
@Ian - Você está certo. Bom ponto. Acho que fiquei muito animado para provar um ponto sobre a simplicidade. Eu testei, mas não completamente. Agora vou consertar ... Gostaria que os dados da amostra tivessem comprovado seu ponto antes de espalhar aquele ovo por todo o meu rosto!
jahroy
Haha, eu sei, eu sei, odeio quando esse tipo de coisa acontece. Parece tão certo, mas algo o altera internamente e não funciona como esperado. É como comparar strings com <ou >. De qualquer forma, gosto da atualização :)
Ian,
1
@Ash - O melhor lugar para procurar é a documentação. Eu gosto da documentação do Mozilla, então quando eu tenho uma dúvida sobre uma função JS, eu sempre procuro no Google "mdn {{function_name}}. " Neste caso, o termo de pesquisa seria "mdn array.sort" que traz você aqui .
jahroy
1
... como você verá na documentação, o método array.sort () recebe uma função como argumento, o que é bastante comum em JavaScript. O método array.sort () é projetado de forma que ele saiba o que fazer com a função passada a ele: ele o usa para comparar seus elementos. Aqui está um violino realmente idiota que fiz para tentar demonstrar como você passa funções como referências ... desculpe, é tão ruim.
jahroy
66

A melhor abordagem seria usar o seguinte, pois pode haver valores repetitivos na primeira coluna.

var arr = [[12, 'AAA'], [12, 'BBB'], [12, 'CCC'],[28, 'DDD'], [18, 'CCC'],[12, 'DDD'],[18, 'CCC'],[28, 'DDD'],[28, 'DDD'],[58, 'BBB'],[68, 'BBB'],[78, 'BBB']];

arr.sort(function(a,b) {
    return a[0]-b[0]
});
Pramod Vemulapalli
fonte
Esta é a resposta correta, leva em consideração os dois dígitos do número. Obrigado!
apelido de
53

tente isso

//WITH FIRST COLUMN
arr = arr.sort(function(a,b) {
    return a[0] - b[0];
});


//WITH SECOND COLUMN
arr = arr.sort(function(a,b) {
    return a[1] - b[1];
});

Observação: a resposta original usou um maior que (>) em vez de menos (-), que é o que os comentários estão chamando de incorreto.

PSR
fonte
8
8 votos positivos para uma solução completamente errada? Eu não posso acreditar nisso. Por favor, leia sobre as funções de comparação e entenda quando elas precisam retornar valores negativos.
Bergi
6
Como disse Bergi, essa não é a solução certa. Embora possa funcionar em muitos casos, haverá momentos em que não funcionará como o esperado e você ficará coçando a cabeça (aconteceu comigo). O cerne do problema é que a função de comparação nesta solução retorna apenas dois estados (verdadeiro / 1, falso / 0), mas deveria retornar três estados (zero, maior que zero e menor que zero).
2015
1
não funcionou para mim. @jahroy é o homem com a resposta correta
erdomester
Apenas uma nota para quem está lendo os comentários: A resposta foi corrigida em 5 de janeiro. Está correta agora (a função de comparação retorna três estados possíveis).
março de
@Bergi, obrigado por apontar isso. (para mim, ele não funciona corretamente no IE11) e não consegui entender (por que funciona no Chrome) até que vi seu comentário. Obrigado!
Alex Nevsky
12

Usando a função de seta e classificando pelo segundo campo de string

var a = [[12, 'CCC'], [58, 'AAA'], [57, 'DDD'], [28, 'CCC'],[18, 'BBB']];
a.sort((a, b) => a[1].localeCompare(b[1]));
console.log(a)

Dinesh Rajan
fonte
10

Se você for como eu, não vai querer passar pela mudança de cada índice toda vez que quiser mudar a coluna pela qual está classificando.

function sortByColumn(a, colIndex){

    a.sort(sortFunction);

    function sortFunction(a, b) {
        if (a[colIndex] === b[colIndex]) {
            return 0;
        }
        else {
            return (a[colIndex] < b[colIndex]) ? -1 : 1;
        }
    }

    return a;
}

var sorted_a = sortByColumn(a, 2);
Charles Clayton
fonte
Acabei de ver sua resposta, depois de redigir uma resposta eu mesmo com exatamente o mesmo raciocínio - com uma pequena diferença - na verdade, retorno a função para classificar diretamente.
Olamotte,
3

Nada de especial, apenas economizando o custo necessário para retornar um valor em um determinado índice de uma matriz.

function sortByCol(arr, colIndex){
    arr.sort(sortFunction)
    function sortFunction(a, b) {
        a = a[colIndex]
        b = b[colIndex]
        return (a === b) ? 0 : (a < b) ? -1 : 1
    }
}
// Usage
var a = [[12, 'AAA'], [58, 'BBB'], [28, 'CCC'],[18, 'DDD']]
sortByCol(a, 0)
console.log(JSON.stringify(a))
// "[[12,"AAA"],[18,"DDD"],[28,"CCC"],[58,"BBB"]]"
Vikas Gautam
fonte
Como essa resposta é diferente da minha aqui ?
Charles Clayton
1
1. você está usando a[colIndex]novamente e novamente, mas estou pegando aqui a = a[colIndex]. É mais eficiente. 2. Estou usando um sabor diferente de if, tornando-o mais curto. 3. Não estou retornando arrcomo resultado de sortByColfunção, o que significa que minha função não pode ser usada para criar outra referência. Espero que ajude!
Vikas Gautam
3

em uma linha:

var cars = [
  {type:"Volvo", year:2016},
  {type:"Saab", year:2001},
  {type:"BMW", year:2010}
]


function myFunction() {
  return cars.sort((a, b)=> a.year - b.year)
}
Jared
fonte
3

Se você deseja classificar com base na primeira coluna (que contém o valor numérico ), tente o seguinte:

arr.sort(function(a,b){
  return a[0]-b[0]
})

Se você deseja classificar com base na segunda coluna (que contém o valor da string ), tente o seguinte:

arr.sort(function(a,b){
  return a[1].charCodeAt(0)-b[1].charCodeAt(0)
})

PS para o segundo caso, você precisa comparar entre seus valores ASCII.

Espero que isto ajude.

Sabbir Ahmed
fonte
0

Como meu caso de uso envolve dezenas de colunas, expandi um pouco a resposta de @jahroy. (também acabei de perceber que @ charles-clayton teve a mesma ideia.)
Passo o parâmetro pelo qual desejo classificar e a função de classificação é redefinida com o índice desejado para a comparação.

var ID_COLUMN=0
var URL_COLUMN=1

findings.sort(compareByColumnIndex(URL_COLUMN))

function compareByColumnIndex(index) {
  return function(a,b){
    if (a[index] === b[index]) {
        return 0;
    }
    else {
        return (a[index] < b[index]) ? -1 : 1;
    }
  }
}
Olamotte
fonte
0

Apoiado nos ombros de charles-clayton e @vikas-gautam, adicionei o teste de cordas que é necessário se uma coluna tem cordas como em OP.

return isNaN(a-b) ? (a === b) ? 0 : (a < b) ? -1 : 1 : a-b  ;

O teste isNaN(a-b)determina se as strings não podem ser transformadas em números. Se eles podem, então oa-b teste é válido.

Observe que classificar uma coluna de tipos mistos sempre fornecerá um resultado divertido, pois o teste de igualdade estrito (a === b)sempre retornará falso. Veja MDN aqui

Este é o script completo com teste Logger - usando Google Apps Script.

function testSort(){

function sortByCol(arr, colIndex){
    arr.sort(sortFunction);
    function sortFunction(a, b) {
        a = a[colIndex];
        b = b[colIndex];
       return isNaN(a-b) ? (a === b) ? 0 : (a < b) ? -1 : 1 : a-b  ;  // test if text string - ie cannot be coerced to numbers.
       // Note that sorting a column of mixed types will always give an entertaining result as the strict equality test will always return false
       // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness

       }
}
// Usage
var a = [ [12,'12', 'AAA'],
          [12,'11', 'AAB'],
          [58,'120', 'CCC'],
          [28,'08', 'BBB'],
          [18,'80', 'DDD'],
        ]
    var arr1 = a.map(function (i){return i;}).sort();  // use map to ensure tests are not corrupted by a sort in-place.

    Logger.log("Original unsorted:\n     " + JSON.stringify(a));
    Logger.log("Vanilla sort:\n     " + JSON.stringify(arr1));
    sortByCol(a, 0);
    Logger.log("By col 0:\n     " + JSON.stringify(a));
    sortByCol(a, 1);
    Logger.log("By col 1:\n     " + JSON.stringify(a));
    sortByCol(a, 2);
    Logger.log("By col 2:\n     " + JSON.stringify(a));

/* vanilla sort returns " [
                            [12,"11","AAB"],
                            [12,"12","AAA"],
                            [18,"80","DDD"],
                            [28,"08","BBB"],
                            [58,"120","CCC"]
                          ]
   if col 0 then returns "[
                            [12,'12',"AAA"],
                            [12,'11', 'AAB'],
                            [18,'80',"DDD"],
                            [28,'08',"BBB"],
                            [58,'120',"CCC"]
                          ]"
   if col 1 then returns "[
                            [28,'08',"BBB"],
                            [12,'11', 'AAB'],
                            [12,'12',"AAA"],
                            [18,'80',"DDD"],
                            [58,'120',"CCC"],

                          ]"
   if col 2 then returns "[
                            [12,'12',"AAA"],
                            [12,'11', 'AAB'],
                            [28,'08',"BBB"],
                            [58,'120',"CCC"],
                            [18,'80',"DDD"],
                          ]"
*/

}
DeeKay789
fonte
Atualização de possível interesse - 2 de julho de 2019. A classificação agora está 'estável'. Leia aqui. (via Mathias BynensVerified @mathias) v8.dev/features/stable-sort
DeeKay789