Como posso classificar uma lista em ordem alfabética usando o jQuery?

233

Estou um pouco fora da minha profundidade aqui e espero que isso seja realmente possível.

Eu gostaria de poder chamar uma função que classificaria todos os itens da minha lista em ordem alfabética.

Eu estive procurando na interface do usuário do jQuery para classificar, mas isso não parece ser o caso. Alguma ideia?

Shog9
fonte
Confira Underscore.js ou Sugar.js .
Kris Khaira #

Respostas:

106

Você não precisa do jQuery para fazer isso ...

function sortUnorderedList(ul, sortDescending) {
  if(typeof ul == "string")
    ul = document.getElementById(ul);

  // Idiot-proof, remove if you want
  if(!ul) {
    alert("The UL object is null!");
    return;
  }

  // Get the list items and setup an array for sorting
  var lis = ul.getElementsByTagName("LI");
  var vals = [];

  // Populate the array
  for(var i = 0, l = lis.length; i < l; i++)
    vals.push(lis[i].innerHTML);

  // Sort it
  vals.sort();

  // Sometimes you gotta DESC
  if(sortDescending)
    vals.reverse();

  // Change the list on the page
  for(var i = 0, l = lis.length; i < l; i++)
    lis[i].innerHTML = vals[i];
}

Fácil de usar...

sortUnorderedList("ID_OF_LIST");

Demonstração ao vivo →

Josh Stodola
fonte
30
Um problema que encontrei com essa abordagem é que, como é apenas o texto que está sendo movido, se você associar dados aos nós DOM usando jQuery.data antes da classificação, essas associações agora apontam para os nós errados após a classificação.
Rudism
13
Mover elementos com innerHTML é uma solução ruim porque eles não são os mesmos elementos após a classificação. Todas as referências existentes aos elementos são perdidas. Todos os ouvintes de eventos vinculados ao JavaScript são perdidos. Seria melhor armazenar os elementos em vez do innerHTML, usar uma função de classificação (vals.sort (função (a, b) {return b.innerHTML <a.innerHTML;})) e appendChild para mover elementos.
Gregers
4
Buhuuu innerHTML! Não use isso. É material proprietário da Microsoft e nunca foi reconhecido pelo W3C.
Steve K
14
IMHO esta é uma resposta horrível. É perfeitamente possível reorganizar nós DOM sem serializá-los e desserializá-los novamente e sem destruir nenhuma propriedade e / ou evento anexado.
Alnitak
4
... mas se você usasse o jQuery, como faria?
26515 Matthew
332

Algo assim:

var mylist = $('#myUL');
var listitems = mylist.children('li').get();
listitems.sort(function(a, b) {
   return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase());
})
$.each(listitems, function(idx, itm) { mylist.append(itm); });

Nesta página: http://www.onemoretake.com/2009/02/25/sorting-elements-with-jquery/

O código acima ordenará sua lista não ordenada com o ID 'myUL'.

OU você pode usar um plugin como o TinySort. https://github.com/Sjeiti/TinySort

SolutionYogi
fonte
6
A última linha pode ser substituída por $ (listitems) .appendTo (mylist); ?
Amir
26
HL Menken tem uma citação que descreve esta solução: "Para todo problema, existe uma solução que é simples, elegante e errada". Esse processo é executado no tempo O (n ^ 2). Não é perceptível em listas relativamente curtas, mas em uma lista que contém mais de 100 elementos, leva de 3 a 4 segundos para concluir a classificação.
Nathan Strong
5
@ Nathan: Sobre "Para todo problema, existe uma solução simples, elegante e errada." - bem, uma solução errada não é elegante.
Johann Philipp Strathausen
18
Algo pode ser elegante e intrigante de se assistir, mas ainda falha. Elegância não implica sucesso.
Jane Panda
13
Esta solução não está errada. Responde à pergunta. O OP não especificou que ele precisava classificar uma lista de mais de 100 itens. Se sua lista nunca exceder 100 itens, esta solução é perfeitamente aceitável. +1 por apontar que a solução é lenta, -1 por declarar uma solução que atenda aos requisitos como 'incorreta'.
Samurai Soul
94
$(".list li").sort(asc_sort).appendTo('.list');
//$("#debug").text("Output:");
// accending sort
function asc_sort(a, b){
    return ($(b).text()) < ($(a).text()) ? 1 : -1;    
}

// decending sort
function dec_sort(a, b){
    return ($(b).text()) > ($(a).text()) ? 1 : -1;    
}

demonstração ao vivo: http://jsbin.com/eculis/876/edit

Jeetendra Chauhan
fonte
15
Esta é a melhor resposta. Eu até gosto lo em uma única linha como esta: $(".list li").sort(function(a, b){return ($(b).text()) < ($(a).text());}).appendTo('.list');. Uma observação, porém: .text()deveria ser.text().toUpperCase()
Jules Colle
Infelizmente, esta solução não funciona no IE, enquanto a resposta PatrickHecks abaixo funciona em todos os navegadores.
PATG
8
Cuidado com o seletor! ".list li" selecionará todas as tags LI descendentes, não apenas os filhos imediatos.
Doug Domeny 1/11/2013
3
@DougDomeny está certo. É melhor ligar $(".list").children(), se possível, ou configurar o seletor com um relacionamento imediato com o filho, como$(".list > li")
mroncetwice
1
BTW Aqui está um link para um violino que eu fiz usando esta resposta. Eu configurei o código como uma função all-in-one: jsfiddle.net/mroncetwice/t0whh6fL
mroncetwice
39

Para que este trabalho funcione com todos os navegadores, incluindo o Chrome, você precisa fazer com que a função de retorno de chamada de sort () retorne -1,0 ou 1.

consulte http://inderpreetsingh.com/2010/12/01/chromes-javascript-sort-array-function-is-different-yet-proper/

function sortUL(selector) {
    $(selector).children("li").sort(function(a, b) {
        var upA = $(a).text().toUpperCase();
        var upB = $(b).text().toUpperCase();
        return (upA < upB) ? -1 : (upA > upB) ? 1 : 0;
    }).appendTo(selector);
}
sortUL("ul.mylist");
PatrickHeck
fonte
2
Todos os elementos li não devem ser removidos do ul.myList antes de anexar os elementos li classificados?
Daud
2
@Daud, os elementos LI não precisam ser removidos explicitamente.
Doug Domeny
1
Usar .localeCompare seria uma melhoria para caracteres não ASCII.
Doug Domeny
1
@DougDomeny, por que os elementos li não precisam ser removidos explicitamente?
bowserm
3
@bowserm, o método appendTo move os elementos DOM em vez de copiá-los.
Doug Domeny
31

Se você estiver usando o jQuery, poderá fazer o seguinte:

$(function() {

  var $list = $("#list");

  $list.children().detach().sort(function(a, b) {
    return $(a).text().localeCompare($(b).text());
  }).appendTo($list);

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<ul id="list">
  <li>delta</li>
  <li>cat</li>
  <li>alpha</li>
  <li>cat</li>
  <li>beta</li>
  <li>gamma</li>
  <li>gamma</li>
  <li>alpha</li>
  <li>cat</li>
  <li>delta</li>
  <li>bat</li>
  <li>cat</li>
</ul>

Observe que retornar 1 e -1 (ou 0 e 1) da função de comparação está absolutamente errado .

Salman A
fonte
7

A resposta da @ SolutionYogi funciona como um encanto, mas parece que o uso de $ .each é menos direto e eficiente do que os itens de lista anexados diretamente:

var mylist = $('#list');
var listitems = mylist.children('li').get();

listitems.sort(function(a, b) {
   return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase());
})

mylist.empty().append(listitems);

Violino

Buzut
fonte
$ ('# list'). empty () -> mylist.empty () seria melhor. Não há necessidade de tocar no DOM novamente.
Jacob van Lingen
Absolutamente, eu apenas consertei!
Buzut
Isso não funciona no Internet Explorer (testado com a versão 10).
Chad Johnson
Acabei de testar com o IE11 e, de fato, não funciona. Mas o código da SolutionYogi não funcionou nem no IE11 ... Isso funcionou para você?
Buzut
1

melhoria baseada na resposta de Jeetendra Chauhan

$('ul.menu').each(function(){
    $(this).children('li').sort((a,b)=>a.innerText.localeCompare(b.innerText)).appendTo(this);
});

por que considero uma melhoria:

  1. usando eachpara suportar a execução em mais de um ul

  2. usar em children('li')vez de ('ul li')é importante porque só queremos processar filhos diretos e não descendentes

  3. usar a função de seta (a,b)=>parece melhor (IE não suportado)

  4. usando baunilha em innerTextvez de $(a).text()para melhorar a velocidade

  5. usar baunilha localeComparemelhora a velocidade em caso de elementos iguais (raro no uso na vida real)

  6. usar em appendTo(this)vez de usar outro seletor garantirá que, mesmo que o seletor capte mais de um ul, nada seja interrompido

oriadam
fonte
0

Eu estava tentando fazer isso sozinho e não estava satisfeito com nenhuma das respostas fornecidas simplesmente porque, acredito, são tempos quadráticos, e preciso fazer isso em listas com centenas de itens.

Acabei estendendo o jquery e minha solução usa o jquery, mas poderia ser facilmente modificado para usar o javascript direto.

Eu só acesso cada item duas vezes e realizo uma classificação linearitmica, portanto, acho que isso deve ser muito mais rápido em grandes conjuntos de dados, embora eu confesse livremente que poderia estar enganado:

sortList: function() {
   if (!this.is("ul") || !this.length)
      return
   else {
      var getData = function(ul) {
         var lis     = ul.find('li'),
             liData  = {
               liTexts : []
            }; 

         for(var i = 0; i<lis.length; i++){
             var key              = $(lis[i]).text().trim().toLowerCase().replace(/\s/g, ""),
             attrs                = lis[i].attributes;
             liData[key]          = {},
             liData[key]['attrs'] = {},
             liData[key]['html']  = $(lis[i]).html();

             liData.liTexts.push(key);

             for (var j = 0; j < attrs.length; j++) {
                liData[key]['attrs'][attrs[j].nodeName] = attrs[j].nodeValue;
             }
          }

          return liData;
       },

       processData = function (obj){
          var sortedTexts = obj.liTexts.sort(),
              htmlStr     = '';

          for(var i = 0; i < sortedTexts.length; i++){
             var attrsStr   = '',
                 attributes = obj[sortedTexts[i]].attrs;

             for(attr in attributes){
                var str = attr + "=\'" + attributes[attr] + "\' ";
                attrsStr += str;
             }

             htmlStr += "<li "+ attrsStr + ">" + obj[sortedTexts[i]].html+"</li>";
          }

          return htmlStr;

       };

       this.html(processData(getData(this)));
    }
}
Quirkles
fonte
0

HTML

<ul id="list">
    <li>alpha</li>
    <li>gamma</li>
    <li>beta</li>
</ul>

Javascript

function sort(ul) {
    var ul = document.getElementById(ul)
    var liArr = ul.children
    var arr = new Array()
    for (var i = 0; i < liArr.length; i++) {
        arr.push(liArr[i].textContent)
    }
    arr.sort()
    arr.forEach(function(content, index) {
        liArr[index].textContent = content
    })
}

sort("list")

JSFiddle Demo https://jsfiddle.net/97oo61nw/

Aqui, pressionamos todos os valores dos lielementos ulcom específico id(que fornecemos como argumento da função) para arrordenar e classificar usando o método sort () , que é classificado alfabeticamente por padrão. Depois que a matriz arré classificada, fazemos um loop nessa matriz usando o método forEach () e apenas substituímos o conteúdo de texto de todos os lielementos pelo conteúdo classificado

Mikhail
fonte