2013-06-12 3 views
0

Je suis désolé mais je pense que je vais devoir coller beaucoup de code dans ma question. Les bonnes nouvelles sont cependant, si vous avez le temps, vous pouvez simplement copier cela dans une application de la console et l'exécuter afin que vous puissiez voir le problème avec les résultats.IComparabale trie dans un ordre inattendu

On m'a donné une liste (et oui, la liste dans le code ci-dessous est en fait la liste!). Essentiellement, on me donnera List<string, string> que j'appellerai List<ColLeft, ColRight> juste pour plus de clarté. ColLeft est déjà groupé et doit rester groupé. ColRight n'est pas alphabétique et doit faire partie de son groupe.

Je suis sur. NET 2.0 et en tant que tel j'ai implémenté IComparable<T>. Cependant, la liste est retournée dans le désordre et je ne comprends pas pourquoi (le même problème persiste sur VS2005 ou VS2010).

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 

namespace SortingLists 
{ 
    class Program 
    { 
     static void Main() 
     { 
      List<ListContents> listContents = ListContents.GetListContents(); 
      WriteOut(listContents); 
      Console.WriteLine("\r\n"); 

      listContents.Sort(); 
      WriteOut(listContents); 
      Console.ReadKey(); 
     } 

     private static void WriteOut(List<ListContents> listContents) 
     { 
      foreach (ListContents content in listContents) 
       Console.WriteLine(content.ColLeft + " --- " + content.ColRight); 
     } 
    } 

    struct ListContents : IComparable<ListContents> 
    { 
    #region Constructor 

    public ListContents(string l, string r) 
    { 
     this.ColLeft = l; 
     this.ColRight = r; 
    } 

    #endregion 

    #region Fields 

    public string ColLeft; 
    public string ColRight; 

    #endregion 

    #region IComparable<ListContents> Members 

    public int CompareTo(ListContents other) 
    { 
     if (this.ColLeft.CompareTo(other.ColLeft) == -1) 
      return this.ColLeft.CompareTo(other.ColLeft); 
     else 
      return this.ColRight.CompareTo(other.ColRight); 
    } 

    #endregion 

    #region Methods 

    public static List<ListContents> GetListContents() 
    { 
     List<ListContents> lcList = new List<ListContents>(); 
     lcList.Add(new ListContents("UFT", "a")); 
     lcList.Add(new ListContents("UFT", "c")); 
     lcList.Add(new ListContents("UFT", "b")); 
     lcList.Add(new ListContents("RT", "f")); 
     lcList.Add(new ListContents("RT", "e"));    
     lcList.Add(new ListContents("RT", "d")); 
     lcList.Add(new ListContents("UT", "m")); 
     lcList.Add(new ListContents("UT", "o")); 
     lcList.Add(new ListContents("UT", "n"));    
     return lcList; 
    } 
} 

Je peux le résoudre si - si je change l'ordre de GetListContents() à quelque chose comme ...

public static List<ListContents> GetListContents() 
    { 
     List<ListContents> lcList = new List<ListContents>(); 
     lcList.Add(new ListContents("UFT", "a")); 
     lcList.Add(new ListContents("UFT", "c")); 
     lcList.Add(new ListContents("UFT", "b")); 
     lcList.Add(new ListContents("RT", "e")); 
     lcList.Add(new ListContents("RT", "f"));//Moved this item 
     lcList.Add(new ListContents("RT", "d")); 
     lcList.Add(new ListContents("UT", "m")); 
     lcList.Add(new ListContents("UT", "o")); 
     lcList.Add(new ListContents("UT", "n"));    
     return lcList; 
    } 

... Ensuite, les résultats sont comme souhaité. Évidemment ce n'est pas une solution car je ne peux pas prédire l'ordre dans lequel la liste va entrer, la seule constante est que ColLeft est groupé.

Quelqu'un peut-il m'aider à comprendre pourquoi ce comportement?

Répondre

1

La liste est retournée en panne car vous n'avez conservé la commande d'origine nulle part. Dans LINQ (3.x .NET), vous pouvez le faire comme suit:

list.GroupBy(x => x.LeftCol) 
    .Select(g => g.OrderBy(x => x.RightCol)) 
    .SelectMany(x => x) 
    .ToList(); 

Dans .NET 2.0, vous devriez faire quelque chose de semblable; c'est-à-dire le premier groupe de LeftCol, puis trier chaque groupe par RightCol, puis concaténer les groupes. Par exemple, si vous contrôlez la classe ListContents, vous pouvez ajouter un champ ou une propriété int Index représentant l'ordre que vous souhaitez conserver (0 pour le premier groupe, 1 pour le deuxième groupe, etc.). puis trier en utilisant un comparateur qui compare d'abord par Index, puis par RightCol:

int index = 0; 
for(int i=0; i<list.Count; i++) 
{ 
    if (i > 0 && list[i].LeftCol != list[i - 1].LeftCol) index++; 
    list[i].Index = index; 
} 

... 

public int CompareTo(ListContents other) 
{ 
    int result = this.Index.CompareTo(other.Index); 
    if (result != 0) return result; 
    return this.ColRight.CompareTo(other.ColRight); 
} 

Si vous ne voulez pas modifier la classe ListContents, vous pourriez faire quelque chose de similaire en enveloppant chaque élément dans un Tuple<int, ListContents> avant le tri.