2011-03-15 4 views
1

J'ai une ArrayList qui contient un grand nombre de chaînes. Il doit être trié sur la base de trois champs (essentiellement trois sous-chaînes) qui sont Nom, Âge et Amt. Âge est la première sous-chaîne (position 0-3), Nom est deuxième (3-6) et Amt est le dernier (6-10). L'ordre dans lequel ces paramètres doivent être classés est très important et est la suivante:ArrayList Tri

d'abord effectuer ascendant Trier par Nom faire alors ascendant Tri âge (qui vient en fait plus tôt dans la sous-chaîne) et ALORS décroissant trier par Amt. C'est tout.

J'ai cette classe

public class ArrComparer : IComparer 
{ 
    public int Compare(object x, object y) 
    { 
     string left = x.ToString(); 
     string right = y.ToString(); 
     string lhs = left.Substring(3, 6); 
     string rhs = right.Substring(3, 6); 
     return lhs.CompareTo(rhs); 
    } 
} 

que j'utilise pour trier basé sur un seul champ - Nom en invoquant

RecordList.Sort(new ArrComparer()); 

Cela me permet correctement basé sorte que un champ. La question est comment puis-je modifier ce code pour me permettre de trier en fonction des trois AT ONCE, dans le bon ordre et en utilisant le mode asc/desc approprié?

Un code ou des astuces seraient grandement appréciés. (En passant, au cas où vous vous demandez l'utilisation générique List<T> n'est pas une option dans ce projet).

+1

Pourquoi une liste générique "n'est pas une option"? Pourquoi n'utilisez-vous pas des objets avec trois champs au lieu de chaînes? –

+0

LINQ est-elle une option? – JasonCoder

+0

Ça vous dérange de dire pourquoi les listes génériques ne sont pas une option? –

Répondre

1

Si vous avez besoin d'un IComparer, essayez quelque chose comme:

public class ArrComparer : IComparer 
{ 
    public int Compare(object x, object y) 
    { 
    string left = x.ToString(); 
    string right = y.ToString(); 
    string leftName = left.Substring([whatever]); 
    string rightName = right.Substring([whatever]); 

    // First try comparing names 
    int result = leftName.CompareTo(rightName); 
    if (result != 0) 
    { 
     return result; 
    } 

    // If that didn't work, compare ages 
    string leftAge = left.Substring([whatever]); 
    string rightAge = right.Substring([whatever]); 
    result = leftAge.CompareTo(rightAge); 
    if (result != 0) 
    { 
     return result; 
    }  

    // Finally compare amounts (descending) 
    string leftAmt = left.Substring([whatever]); 
    string rightAmt = right.Substring([whatever]); 
    result = -leftAmt.CompareTo(rightAmt); // Minus for descending 

    return result; 
    } 
} 
0

Vous pouvez augmenter votre valeur ArrCompare avec si des instructions telles que if (rhs == lhs) se cumulent avec d'autres parties de chaîne. Accen deccend est meeter de retour -1 ou 1

7

ArrayList implémente encore IEnumerable, ce qui signifie que vous pouvez utiliser simple orderby() et ThenBy() extensions de LINQ:

RecordList = new ArrayList(
     RecordList.Cast<string>().OrderBy(s => s.Substring(3,3)) 
        .ThenBy(s => int.Parse(s.Substring(0,3))) 
        .ThenByDescending(s => double.Parse(s.Substring(6,4))) 
      .ToArray()); 

D'autres façons d'exprimer inclure la construction d'un plus compliqué .OrderBy() ou en utilisant un type anonyme pour composer votre chaîne comme un objet:

RecordList = new ArrayList(
     Record.Cast<string>().Select(s => new {source = s, age = int.Parse(s.Substring(0, 3)), name = s.Substring(3,3), amt = double.Parse(s.Substring(6,4))}) 
      .OrderBy(o => o.name) 
      .ThenBy(o => o.age) 
      .ThenByDescending(o => o.amt) 
      .Select(o => o.source).ToArray()); 

J'aime cette option, car il vous met en place pour commencer à penser à des objets termes. Jouez vos cartes correctement et vous pouvez passer cette dernière projection .Select() pour conserver les objets plutôt que de retourner aux chaînes, ce qui vous évitera d'avoir à refaire toute cette analyse plus tard.

Si ceux-ci ne sont pas une option (peut-être pour la même raison que vous ne pouvez pas utiliser la liste < T>), il est facile de modifier votre méthode existante comparer comme ceci:

public class ArrComparer : IComparer 
{ 
    public int Compare(object x, object y) 
    { 
     int result; 
     string left = x.ToString(); 
     string right = y.ToString(); 
     string lhs1 = left.Substring(3, 3); 
     string rhs1 = right.Substring(3, 3); 
     result = lhs1.CompareTo(rhs1); 

     if (result == 0) 
     { 
      int lhs2 = int.Parse(left.Substring(0,3)); 
      int rhs2 = int.Parse(right.Substring(0,3)); 
      result = lhs2.CompareTo(rhs2); 
     } 

     if (result == 0) 
     { 
      double lhs3 = double.Parse(left.Substring(6,4)); 
      double rhs3 = double.Parse(right.Substring(6,4)); 
      result = rhs3.CompareTo(lhs3); 
     } 

     return result; 
    } 
} 
+1

cela ne ferait pas un * en place * le tri quoique demandé dans la question OP – BrokenGlass

+0

@Joel: vous devriez utiliser 'Substring (3, 3)' au lieu de 'Substring (3, 6)', comme le second argument est la longueur . La même chose pour 'Substring (6, 10)'. – Vlad

+0

@Vlad - bien sûr, vous avez raison à ce sujet. Pour ma défense, je prenais ma queue du code dans la question originale. –

2

Vous pouvez comparer partie par une partie:

string left = (string)x; 
string right = (string)y; 

string lname = left.Substring(3, 3); 
string rname = right.Substring(3, 3); 
int result = lname.CompareTo(rname); 
if (result != 0) return result; 

string lage = left.Substring(0, 3); 
string rage = right.Substring(0, 3); 
int result = lage.CompareTo(rage); 
if (result != 0) return result; 

string lamt = left.Substring(6); 
string ramt = right.Substring(6); 
return -lamt.CompareTo(ramt); 
1

I wo Uld recommande de stocker vos enregistrements dans un objet, et rendre ceux-ci comparables à la place.

Afin de comparer les trois champs en utilisant la même méthode que celle que vous utilisez actuellement, il suffit d'extraire les trois données et de faire une comparaison complète.

public class ArrComparer : IComparer 
{ 
    public int Compare(object x, object y) 
    { 
     string left = x.ToString(); 
     string right = y.ToString(); 

     // Note I assumed indexes since yours were overlapping. 
     string lage = left.Substring(0, 3); 
     string lname = left.Substring(3, 3); 
     string lamt = left.Substring(7, 3); 

     string rage = left.Substring(0, 3); 
     string rname = left.Substring(3, 3); 
     string ramt = left.Substring(7, 3); 

     // Compare name first, if one is greater return 
     int result = lname.CompareTo(rname); 
     if (result != 0) 
      return result; 

     // else compare age, if one is greater return 
     result = lage.CompareTo(rage) 
     if (result != 0) 
      return result; 

     // else compare amt if one is greater return 
     result = lamt.CompareTo(ramt) 
     if (result != 0) 
      return result; 

     // else they are equal 
     return 0; 
    } 
} 
+0

vous devriez utiliser 'Substring (3, 3)' au lieu de 'Substring (3, 6)', car le second argument est length. La même chose pour 'Substring (7, 10)'. De même, 'Substring (0, 2)' semble être faux. – Vlad

+0

@Vlad Oups! Merci pour l'information. :) – Cody

+0

Merci pour toutes les réponses, les gars! Je vais avoir besoin d'un peu de temps pour les analyser et les essayer, puis je reviendrai avec des commentaires. – GonzoKnight