Posts Tagged ‘Sort’

Sorted…!!! :)

Thursday, October 29th, 2009
Autor: cesar.silva


Uma vez numa entrevista de trabalho perguntaram-me:

“Se tiveres um problema em mãos e não souberes como o resolver, o que fazes?!”

Comecei por achar a pergunta um tanto ou quanto estranha e vaga mas era nitidamente uma daquelas que poderia fazer a diferença entre ser contratado ou não. A resposta que o entrevistador estava à procura era:

“Quando tudo o resto falhar e todos os que conhecer não souberem… vou ao Google! Se não estiver no Google, então é porque não existe…”

Há uns tempos atrás andei à procura de uma forma de ordenar listas/colecções/arrays por múltiplas colunas. Nesta altura Linq não era opção. Como qualquer bom programador, fui ao Google procurar por quem já tivesse partido pedra e encontrei um artigo muito interessante:

Sorting with Objects on multiple fields
http://www.codeproject.com/KB/recipes/Sorting_with_Objects.aspx

“Nada como o belo de um copy/paste para dentro do nosso projecto para resolver o problema!” – pensei.

O código estava interessante, eu faria algumas coisas de forma diferente e rapidamente deu para perceber que não funcionava assim tão bem como haviam vendido. A ordenação só funcionava correctamente para o primeiro campo. Como o meu amigo Antoine diria:

“Não são bugs… são novas e entusiasmantes oportunidades de refactoring!” icon smile Sorted…!!! :)

Dito, feito… e resolvi criar a minha própria classe de ordenação utilizando reflection e alguma recursividade.

Geek stuff!

Aquilo que eu queria era no fundo ordenar um array de objectos (que a título de exemplo são os developers da fullsix)

// Initialize array
var fullsixEmployees = new[] {
    new FullsixEmployee {
        Id = 1,
        Name = "César Silva",
        Birthdate = new DateTime(1977, 7, 16),
        IsPortuguese = true
    },
    new FullsixEmployee {
    ...
    ...
    

A ideia era conseguir dizer de alguma forma que queria ordenar este array por um número ilimitado de propriedades e determinar a direcção dessa ordenação.

// Sort the fullsix employee's array
Array.Sort(fullsixEmployees, new MultiColumnComparer<FullsixEmployee>("IsPortuguese DESC, Birthdate"));

A “magia” reside basicamente nos 2 métodos que determinam a comparação entre as propriedades de cada objecto. Ao invocar o método Sort da classe Array, passamos como argumentos o array a ordenar, seguido de um IComparer<T> que neste caso é a nossa classe MultiColumnComparer.

public int Compare(T x, T y)
{

  // Initialize the source object's type
  var columnsOrder = GetColumnsOrder(this.OrderExpression);
  var comparissonResult = 0;

  if (0 < columnsOrder.Count)
      comparissonResult = ColumnCompare(x, y, columnsOrder, 0);

  return comparissonResult;
}

Após esta primeira invocação há que chamar o segundo método de nome ColumnCompare de forma a permitir a recursividade entre as várias propriedades (caso a comparação de determinada propriedade resulte em igualdade).

public int ColumnCompare(T x, T y, IList<Column> columnsOrder, int columnIndex)
{

  // Get the object's type
  var sourceType = x.GetType();
  var columnProperty = sourceType.GetProperty(columnsOrder[columnIndex].Name);

  // Initialize the comparisson result object
  var comparissonResult = 0;
  comparissonResult = Comparer.DefaultInvariant.Compare(columnProperty.GetValue(x, null), columnProperty.GetValue(y, null));

  // Invert the order if descending
  if (columnsOrder[columnIndex].Direction.Equals(ColumnDirection.Descending))
      comparissonResult = -comparissonResult;

  // If the properties are equal then go to the next property (if available)
  if (comparissonResult.Equals(0) && columnIndex < columnsOrder.Count - 1)
      return ColumnCompare(x, y, columnsOrder, columnIndex + 1);

  return comparissonResult;
}

Através de reflection conseguimos aceder às propriedades dos objectos a serem comparados e através de um ciclo recursivo aceder aos valores de cada uma delas para estabelecer as comparações. O ciclo mantém-se enquanto existir igualdade entre as propriedades a serem comparadas e a direcção da comparação é determinada pela negação do resultado, caso seja descendente.

Acabou por ser um exercício engraçado para brincar mais uma vez com a ordenação de listas/colecções/arrays.

Para quem quiser os ficheiros com a implementação deste exercício, pode retirá-los em Source code.


Better Tag Cloud