Comment trier les chaînes par ordre alphabétique tout en tenant compte de la valeur lorsqu'une chaîne est numérique?

100

J'essaie de trier un tableau de nombres qui sont des chaînes et j'aimerais qu'ils trient numériquement.

Le hic, c'est que je ne peux pas convertir les nombres en int .

Voici le code:

string[] things= new string[] { "105", "101", "102", "103", "90" };

foreach (var thing in things.OrderBy(x => x))
{
    Console.WriteLine(thing);
}

sortie: 101, 102, 103, 105, 90

Je voudrais: 90, 101, 102, 103, 105

EDIT: La sortie ne peut pas être 090, 101, 102 ...

Mise à jour de l'exemple de code pour dire «choses» au lieu de «tailles». Le tableau peut être quelque chose comme ceci:

string[] things= new string[] { "paul", "bob", "lauren", "007", "90" };

Cela signifie qu'il doit être trié par ordre alphabétique et par numéro:

007, 90, bob, lauren, paul

sf.
la source
8
Pourquoi ne pouvez-vous pas les convertir en int?
Femaref
1
"tailles" peut être autre chose comme "nom". L'exemple de code est simplement simplifié.
sf.
2
L'un des nombres sera-t-il négatif? Seront-ils tous des nombres entiers? Quelle est la plage des entiers?
Eric Lippert
"choses" peut être n'importe quel type de chaîne. Je voudrais que la liste soit triée logiquement à une personne non informatisée. Les nombres négatifs doivent être avant les positifs. En termes de longueur de chaîne, ce ne sera pas plus de 100 caractères.
sf.
5
Jusqu'où voulez-vous aller? Devrait image10venir après image2? Devrait Januaryvenir avant February?
svick

Réponses:

104

Passez un comparateur personnalisé dans OrderBy. Enumerable.OrderBy vous permettra de spécifier n'importe quel comparateur de votre choix .

Voici une façon de faire cela:

void Main()
{
    string[] things = new string[] { "paul", "bob", "lauren", "007", "90", "101"};

    foreach (var thing in things.OrderBy(x => x, new SemiNumericComparer()))
    {    
        Console.WriteLine(thing);
    }
}


public class SemiNumericComparer: IComparer<string>
{
    /// <summary>
    /// Method to determine if a string is a number
    /// </summary>
    /// <param name="value">String to test</param>
    /// <returns>True if numeric</returns>
    public static bool IsNumeric(string value)
    {
        return int.TryParse(value, out _);
    }

    /// <inheritdoc />
    public int Compare(string s1, string s2)
    {
        const int S1GreaterThanS2 = 1;
        const int S2GreaterThanS1 = -1;

        var IsNumeric1 = IsNumeric(s1);
        var IsNumeric2 = IsNumeric(s2);

        if (IsNumeric1 && IsNumeric2)
        {
            var i1 = Convert.ToInt32(s1);
            var i2 = Convert.ToInt32(s2);

            if (i1 > i2)
            {
                return S1GreaterThanS2;
            }

            if (i1 < i2)
            {
                return S2GreaterThanS1;
            }

            return 0;
        }

        if (IsNumeric1)
        {
            return S2GreaterThanS1;
        }

        if (IsNumeric2)
        {
            return S1GreaterThanS2;
        }

        return string.Compare(s1, s2, true, CultureInfo.InvariantCulture);
    }
}
Jeff Paulsen
la source
1
Pour l'entrée donnée, cela produit le même résultat que la réponse de Recursive, qui implique PadLeft (). Je suppose que votre entrée est en fait plus complexe que ne le montre cet exemple, auquel cas un comparateur personnalisé est la voie à suivre.
Jeff Paulsen
À votre santé. Cette solution fonctionne et semble être un moyen facile à lire et à mettre en œuvre. +1 pour me montrer que vous pouvez utiliser IComparer sur OrderBy :)
sf.
17
La IsNumericméthode est mauvaise, un codage piloté par exception est toujours mauvais. Utilisez int.TryParseplutôt. Essayez votre code avec une longue liste et cela prendra une éternité.
Nean Der Thal
Si cela est utile, j'ai ajouté une extension à cette version ici qui ajoute la prise en charge du tri avec des mots. Pour mes besoins, le fractionnement sur les espaces était suffisant, et je n'avais guère besoin de me soucier des mots à usage mixte (par exemple test12 vs test3),
matt.bungard
@NeanDerThal Je suis presque sûr que ce n'est que lent / mauvais traitement de nombreuses exceptions dans une boucle, si vous déboguez ou si vous accédez à l'objet Exception.
Kelly Elton
90

Remplissez simplement avec des zéros de la même longueur:

int maxlen = sizes.Max(x => x.Length);
var result = sizes.OrderBy(x => x.PadLeft(maxlen, '0'));
récursif
la source
+1 pour une solution simple, pinailler serait (déjà fait dans le montage, gentil)
Marino Šimić
Bonne idée, mais le problème suivant est que je dois afficher ces valeurs pour que le "90" soit un "90", pas un "090"
sf.
6
@sf: Essayez-le, vous aimerez peut-être le résultat. N'oubliez pas que la clé de commande n'est pas la chose commandée. Si j'ai dit de commander une liste de clients par nom de famille, alors j'obtiens une liste de clients, pas une liste de noms de famille. Si vous dites d'ordonner une liste de chaînes par une chaîne transformée, le résultat est la liste ordonnée des chaînes d'origine, et non des chaînes transformées.
Eric Lippert
J'ai dû ajouter "tailles = tailles. Est-ce normal ou faut-il modifier la réponse?
gorgabal le
1
@gorgabal: En général, réassigner à sizesne fonctionnerait pas non plus, car le résultat est d'un type différent. La réponse est en quelque sorte abrégée, en ce que la deuxième ligne montre le résultat sous forme d'expression, mais c'est au lecteur d'en faire quelque chose. J'ai ajouté une autre affectation de variable pour rendre cela plus clair.
récursif le
74

Et, que diriez-vous de cela ...

string[] sizes = new string[] { "105", "101", "102", "103", "90" };

var size = from x in sizes
           orderby x.Length, x
           select x;

foreach (var p in size)
{
    Console.WriteLine(p);
}
shenhengbin
la source
hehe, j'aime vraiment celui-ci - très intelligent. Désolé si je n'ai pas fourni l'ensemble complet des données initiales
sf.
3
C'est juste comme l'option pad ci-dessus seulement beaucoup mieux IMO.
dudeNumber4
3
var taille = tailles.OrderBy (x => x.Longueur) .ThenBy (x => x);
Phillip Davis
1
Mais cela mélanger les chaînes alphabétiques comme ceci: "b", "ab", "101", "103", "bob", "abcd".
Andrew le
67

La valeur est une chaîne

List = List.OrderBy(c => c.Value.Length).ThenBy(c => c.Value).ToList();

Travaux

Écho
la source
2
Cette réponse est ma préférée.
LacOniC
2
Merci je viens de découvrir que sort une méthode "ThenBy".
ganchito55
Cela fonctionne très bien pour mon cas d'utilisation, où l'entrée est au format nouveaustring[] { "Object 1", "Object 9", "Object 14" }
thelem
2
C'est la meilleure réponse. Cela fonctionne et c'est un bon apprentissage. Merci !!
JuilletOrdinaire
1
Mais cela mélanger les chaînes alphabétiques comme ceci: "b", "ab", "101", "103", "bob", "abcd".
Andrew le
13

Il existe une fonction native dans Windows StrCmpLogicalWqui comparera les nombres en chaînes sous forme de nombres au lieu de lettres. Il est facile de créer un comparateur qui appelle cette fonction et l'utilise pour ses comparaisons.

public class StrCmpLogicalComparer : Comparer<string>
{
    [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
    private static extern int StrCmpLogicalW(string x, string y);

    public override int Compare(string x, string y)
    {
        return StrCmpLogicalW(x, y);
    }
}

Cela fonctionne même sur des chaînes contenant à la fois du texte et des nombres. Voici un exemple de programme qui montrera la différence entre le tri par défaut et le StrCmpLogicalWtri

class Program
{
    static void Main()
    {
        List<string> items = new List<string>()
        {
            "Example1.txt", "Example2.txt", "Example3.txt", "Example4.txt", "Example5.txt", "Example6.txt", "Example7.txt", "Example8.txt", "Example9.txt", "Example10.txt",
            "Example11.txt", "Example12.txt", "Example13.txt", "Example14.txt", "Example15.txt", "Example16.txt", "Example17.txt", "Example18.txt", "Example19.txt", "Example20.txt"
        };

        items.Sort();

        foreach (var item in items)
        {
            Console.WriteLine(item);
        }

        Console.WriteLine();

        items.Sort(new StrCmpLogicalComparer());

        foreach (var item in items)
        {
            Console.WriteLine(item);
        }
        Console.ReadLine();
    }
}

quelles sorties

Example1.txt
Example10.txt
Example11.txt
Example12.txt
Example13.txt
Example14.txt
Example15.txt
Example16.txt
Example17.txt
Example18.txt
Example19.txt
Example2.txt
Example20.txt
Example3.txt
Example4.txt
Example5.txt
Example6.txt
Example7.txt
Example8.txt
Example9.txt

Example1.txt
Example2.txt
Example3.txt
Example4.txt
Example5.txt
Example6.txt
Example7.txt
Example8.txt
Example9.txt
Example10.txt
Example11.txt
Example12.txt
Example13.txt
Example14.txt
Example15.txt
Example16.txt
Example17.txt
Example18.txt
Example19.txt
Example20.txt
Scott Chamberlain
la source
J'aurais aimé qu'il soit plus facile d'utiliser les bibliothèques système en C #
Kyle Delaney
Cela aurait été parfait, mais il ne gère malheureusement pas les nombres négatifs. -1 0 10 2est trié comme0 -1 2 10
nphx
5

essaye ça

sizes.OrderBy(x => Convert.ToInt32(x)).ToList<string>();

Remarque: cela sera utile lorsque tous sont convertibles en chaîne en int .....

Pranay Rana
la source
1
cela convertit un peu la chaîne en un entier.
Femaref
1
"tailles" peut également être non numérique
sf.
Pour "LINQ to SQL", n'oubliez pas le ToList()précédent =>sizes.ToList().OrderBy(x => Convert.ToInt32(x))
A. Morel
5

Je suppose que ce sera beaucoup plus bon si la chaîne contient des chiffres. J'espère que cela aidera.

PS: Je ne suis pas sûr des performances ou des valeurs de chaîne compliquées, mais cela fonctionnait bien quelque chose comme ceci:

lorem ipsum
lorem ipsum 1
lorem ipsum 2
lorem ipsum 3
...
lorem ipsum 20
lorem ipsum 21

public class SemiNumericComparer : IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        int s1r, s2r;
        var s1n = IsNumeric(s1, out s1r);
        var s2n = IsNumeric(s2, out s2r);

        if (s1n && s2n) return s1r - s2r;
        else if (s1n) return -1;
        else if (s2n) return 1;

        var num1 = Regex.Match(s1, @"\d+$");
        var num2 = Regex.Match(s2, @"\d+$");

        var onlyString1 = s1.Remove(num1.Index, num1.Length);
        var onlyString2 = s2.Remove(num2.Index, num2.Length);

        if (onlyString1 == onlyString2)
        {
            if (num1.Success && num2.Success) return Convert.ToInt32(num1.Value) - Convert.ToInt32(num2.Value);
            else if (num1.Success) return 1;
            else if (num2.Success) return -1;
        }

        return string.Compare(s1, s2, true);
    }

    public bool IsNumeric(string value, out int result)
    {
        return int.TryParse(value, out result);
    }
}
Cansın Şenalioğlu
la source
Exactement ce que je cherchais. Je vous remercie!
klugerama
4

Vous dites que vous ne pouvez pas convertir les nombres en int car le tableau peut contenir des éléments qui ne peuvent pas être convertis en int, mais il n'y a aucun mal à essayer:

string[] things = new string[] { "105", "101", "102", "103", "90", "paul", "bob", "lauren", "007", "90" };
Array.Sort(things, CompareThings);

foreach (var thing in things)
    Debug.WriteLine(thing);

Puis comparez comme ceci:

private static int CompareThings(string x, string y)
{
    int intX, intY;
    if (int.TryParse(x, out intX) && int.TryParse(y, out intY))
        return intX.CompareTo(intY);

    return x.CompareTo(y);
}

Sortie: 007, 90, 90, 101, 102, 103, 105, bob, lauren, paul

Ulf Kristiansen
la source
Btw, j'ai utilisé Array.Sort pour plus de simplicité, mais vous pouvez utiliser la même logique dans un IComparer et utiliser OrderBy.
Ulf Kristiansen
Cette solution semble plus rapide qu'avec IComparer (mon avis). 15000 résultats et je pense que cela donne environ une seconde différence.
Jason Foglia
3

Cela semble une demande étrange et mérite une solution étrange:

string[] sizes = new string[] { "105", "101", "102", "103", "90" };

foreach (var size in sizes.OrderBy(x => {
    double sum = 0;
    int position = 0;
    foreach (char c in x.ToCharArray().Reverse()) {
        sum += (c - 48) * (int)(Math.Pow(10,position));
        position++;
    }
    return sum;
}))

{
    Console.WriteLine(size);
}
Marino Šimić
la source
Je voulais dire 0x30 bien sûr. En outre, le tableau peut toujours contenir une chaîne non numérique, pour laquelle la solution produira des résultats intéressants.
Femaref
Et notez que le -48 ou pas ne change absolument rien, nous pourrions directement utiliser la valeur entière du char, alors supprimez -48 si cela vous dérange ...
Marino Šimić
La valeur char est 0x30, si vous convertissez cela en int, ce sera toujours 0x30, ce qui n'est pas le nombre 0.
Femaref
La seule chose convertie en entier est le double qui est renvoyé par MathPow
Marino Šimić
femaref peu importe que ce soit zéro ou non, le système décadique s'en charge, cela pourrait être un Đ si vous le souhaitez, la seule chose qui compte que les nombres soient dans l'ordre croissant dans le jeu de caractères, et qui sont moins que 10
Marino Šimić
3

Ce site traite du tri alphanumérique et triera les nombres dans un sens logique au lieu d'un sens ASCII. Il prend également en compte les alphas qui l'entourent:

http://www.dotnetperls.com/alphanumeric-sorting

EXEMPLE:

  • C: /TestB/333.jpg
  • 11
  • C: /TestB/33.jpg
  • 1
  • C: /TestA/111.jpg
  • 111F
  • C: /TestA/11.jpg
  • 2
  • C: /TestA/1.jpg
  • 111D
  • 22
  • 111Z
  • C: /TestB/03.jpg

  • 1
  • 2
  • 11
  • 22
  • 111D
  • 111F
  • 111Z
  • C: /TestA/1.jpg
  • C: /TestA/11.jpg
  • C: /TestA/111.jpg
  • C: /TestB/03.jpg
  • C: /TestB/33.jpg
  • C: /TestB/333.jpg

Le code est comme suit:

class Program
{
    static void Main(string[] args)
    {
        var arr = new string[]
        {
           "C:/TestB/333.jpg",
           "11",
           "C:/TestB/33.jpg",
           "1",
           "C:/TestA/111.jpg",
           "111F",
           "C:/TestA/11.jpg",
           "2",
           "C:/TestA/1.jpg",
           "111D",
           "22",
           "111Z",
           "C:/TestB/03.jpg"
        };
        Array.Sort(arr, new AlphaNumericComparer());
        foreach(var e in arr) {
            Console.WriteLine(e);
        }
    }
}

public class AlphaNumericComparer : IComparer
{
    public int Compare(object x, object y)
    {
        string s1 = x as string;
        if (s1 == null)
        {
            return 0;
        }
        string s2 = y as string;
        if (s2 == null)
        {
            return 0;
        }

        int len1 = s1.Length;
        int len2 = s2.Length;
        int marker1 = 0;
        int marker2 = 0;

        // Walk through two the strings with two markers.
        while (marker1 < len1 && marker2 < len2)
        {
            char ch1 = s1[marker1];
            char ch2 = s2[marker2];

            // Some buffers we can build up characters in for each chunk.
            char[] space1 = new char[len1];
            int loc1 = 0;
            char[] space2 = new char[len2];
            int loc2 = 0;

            // Walk through all following characters that are digits or
            // characters in BOTH strings starting at the appropriate marker.
            // Collect char arrays.
            do
            {
                space1[loc1++] = ch1;
                marker1++;

                if (marker1 < len1)
                {
                    ch1 = s1[marker1];
                }
                else
                {
                    break;
                }
            } while (char.IsDigit(ch1) == char.IsDigit(space1[0]));

            do
            {
                space2[loc2++] = ch2;
                marker2++;

                if (marker2 < len2)
                {
                    ch2 = s2[marker2];
                }
                else
                {
                    break;
                }
            } while (char.IsDigit(ch2) == char.IsDigit(space2[0]));

            // If we have collected numbers, compare them numerically.
            // Otherwise, if we have strings, compare them alphabetically.
            string str1 = new string(space1);
            string str2 = new string(space2);

            int result;

            if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
            {
                int thisNumericChunk = int.Parse(str1);
                int thatNumericChunk = int.Parse(str2);
                result = thisNumericChunk.CompareTo(thatNumericChunk);
            }
            else
            {
                result = str1.CompareTo(str2);
            }

            if (result != 0)
            {
                return result;
            }
        }
        return len1 - len2;
    }
}
Chad Kuehn
la source
2

La réponse donnée par Jeff Paulsen est correcte mais le Comprarerpeut être beaucoup simplifié à ceci:

public class SemiNumericComparer: IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        if (IsNumeric(s1) && IsNumeric(s2))
          return Convert.ToInt32(s1) - Convert.ToInt32(s2)

        if (IsNumeric(s1) && !IsNumeric(s2))
            return -1;

        if (!IsNumeric(s1) && IsNumeric(s2))
            return 1;

        return string.Compare(s1, s2, true);
    }

    public static bool IsNumeric(object value)
    {
        int result;
        return Int32.TryParse(value, out result);
    }
}

Cela fonctionne car la seule chose qui est vérifiée pour le résultat de la Comparerest si le résultat est plus grand, plus petit ou égal à zéro. On peut simplement soustraire les valeurs d'un autre et n'a pas à gérer les valeurs de retour.

De plus, la IsNumericméthode ne devrait pas avoir à utiliser de try-bloc et peut bénéficier deTryParse .

Et pour ceux qui ne sont pas sûrs: Ce comparateur triera les valeurs de manière à ce que les valeurs non numériques soient toujours ajoutées à la fin de la liste. Si on les veut au début, le deuxième et le troisième ifbloc doivent être échangés.

k-écrou
la source
Comme l'appel de la méthode TryParse a probablement une surcharge, je stockerais d'abord les valeurs isNumeric pour s1 et s2 dans des valeurs booléennes et ferais la comparaison sur celles-ci à la place. De cette façon, ils ne sont pas évalués plusieurs fois.
Optavius
1

Essaye ça :

string[] things= new string[] { "105", "101", "102", "103", "90" };

int tmpNumber;

foreach (var thing in (things.Where(xx => int.TryParse(xx, out tmpNumber)).OrderBy(xx =>     int.Parse(xx))).Concat(things.Where(xx => !int.TryParse(xx, out tmpNumber)).OrderBy(xx => xx)))
{
    Console.WriteLine(thing);
}
TMAmine
la source
1
public class NaturalSort: IComparer<string>
{
          [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
          public static extern int StrCmpLogicalW(string x, string y);

          public int Compare(string x, string y)
          {
                 return StrCmpLogicalW(x, y);
          }
}

arr = arr.OrderBy (x => x, nouveau NaturalSort ()). ToArray ();

La raison pour laquelle j'en avais besoin était d'être classé dans un répertoire dont les noms de fichiers commençaient par un nombre:

public static FileInfo[] GetFiles(string path)
{
  return new DirectoryInfo(path).GetFiles()
                                .OrderBy(x => x.Name, new NaturalSort())
                                .ToArray();
}
Neil M
la source
0
Try this out..  



  string[] things = new string[] { "paul", "bob", "lauren", "007", "90", "-10" };

        List<int> num = new List<int>();
        List<string> str = new List<string>();
        for (int i = 0; i < things.Count(); i++)
        {

            int result;
            if (int.TryParse(things[i], out result))
            {
                num.Add(result);
            }
            else
            {
                str.Add(things[i]);
            }


        }

Maintenant, triez les listes et fusionnez-les ...

        var strsort = from s in str
                      orderby s.Length
                      select s;

        var numsort = from n in num
                     orderby n
                     select n;

        for (int i = 0; i < things.Count(); i++)
        {

         if(i < numsort.Count())
             things[i] = numsort.ElementAt(i).ToString();
             else
             things[i] = strsort.ElementAt(i - numsort.Count());               
               }

J'ai jsut essayé d'apporter une contribution à cette question intéressante ...

Syeda
la source
0

Ma solution préférée (si toutes les chaînes sont uniquement numériques):

// Order by numerical order: (Assertion: all things are numeric strings only) 
foreach (var thing in things.OrderBy(int.Parse))
{
    Console.Writeline(thing);
}
Bug Raptor
la source
0
public class Test
{
    public void TestMethod()
    {
        List<string> buyersList = new List<string>() { "5", "10", "1", "str", "3", "string" };
        List<string> soretedBuyersList = null;

        soretedBuyersList = new List<string>(SortedList(buyersList));
    }

    public List<string> SortedList(List<string> unsoredList)
    {
        return unsoredList.OrderBy(o => o, new SortNumericComparer()).ToList();
    }
}

   public class SortNumericComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        int xInt = 0;
        int yInt = 0;
        int result = -1;

        if (!int.TryParse(x, out xInt))
        {
            result = 1;
        }

        if(int.TryParse(y, out yInt))
        {
            if(result == -1)
            {
                result = xInt - yInt;
            }
        }
        else if(result == 1)
        {
             result = string.Compare(x, y, true);
        }

        return result;
    }
}
Kumar
la source
Pouvez-vous expliquer votre code? Les réponses contenant uniquement du code sont susceptibles d'être supprimées.
Wai Ha Lee
Le message de Jeff Paulsen m'a aidé à implémenter IComparer <string> pour résoudre mon problème de douleur. .
kumar
0

Développant la réponse de Jeff Paulsen. Je voulais m'assurer que peu importe le nombre de groupes de nombres ou de caractères dans les chaînes:

public class SemiNumericComparer : IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        if (int.TryParse(s1, out var i1) && int.TryParse(s2, out var i2))
        {
            if (i1 > i2)
            {
                return 1;
            }

            if (i1 < i2)
            {
                return -1;
            }

            if (i1 == i2)
            {
                return 0;
            }
        }

        var text1 = SplitCharsAndNums(s1);
        var text2 = SplitCharsAndNums(s2);

        if (text1.Length > 1 && text2.Length > 1)
        {

            for (var i = 0; i < Math.Max(text1.Length, text2.Length); i++)
            {

                if (text1[i] != null && text2[i] != null)
                {
                    var pos = Compare(text1[i], text2[i]);
                    if (pos != 0)
                    {
                        return pos;
                    }
                }
                else
                {
                    //text1[i] is null there for the string is shorter and comes before a longer string.
                    if (text1[i] == null)
                    {
                        return -1;
                    }
                    if (text2[i] == null)
                    {
                        return 1;
                    }
                }
            }
        }

        return string.Compare(s1, s2, true);
    }

    private string[] SplitCharsAndNums(string text)
    {
        var sb = new StringBuilder();
        for (var i = 0; i < text.Length - 1; i++)
        {
            if ((!char.IsDigit(text[i]) && char.IsDigit(text[i + 1])) ||
                (char.IsDigit(text[i]) && !char.IsDigit(text[i + 1])))
            {
                sb.Append(text[i]);
                sb.Append(" ");
            }
            else
            {
                sb.Append(text[i]);
            }
        }

        sb.Append(text[text.Length - 1]);

        return sb.ToString().Split(' ');
    }
}

J'ai également pris SplitCharsAndNums à partir d'une page SO après l'avoir modifiée pour traiter les noms de fichiers.

Tod
la source
-1

Même si c'est une vieille question, j'aimerais donner une solution:

string[] things= new string[] { "105", "101", "102", "103", "90" };

foreach (var thing in things.OrderBy(x => Int32.Parse(x) )
{
    Console.WriteLine(thing);
}

Woha assez simple non? :RÉ

Oscar Ortiz
la source
-1
namespace X
{
    public class Utils
    {
        public class StrCmpLogicalComparer : IComparer<Projects.Sample>
        {
            [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
            private static extern int StrCmpLogicalW(string x, string y);


            public int Compare(Projects.Sample x, Projects.Sample y)
            {
                string[] ls1 = x.sample_name.Split("_");
                string[] ls2 = y.sample_name.Split("_");
                string s1 = ls1[0];
                string s2 = ls2[0];
                return StrCmpLogicalW(s1, s2);
            }
        }

    }
}
hamid ramezanali
la source