1

I want to sort a string using indexes, but it's not working after 10th index, 10th/later indexes added after 1st index in the list after using Sort() method.

I have tried below code, but it's not working.

List<string> stringList = new List<string>();

foreach (ManagementObject disk in objectSearcher.Get() )
{
    stringList.Add(string.Format("{0, -15} {1,-35} {2, -20}",
                                disk.GetPropertyValue("Index"),
                                disk.GetPropertyValue("Model"),
                                diskSize));
}
stringList.Sort();

In the above scenario, the code is working fine for 0-9 indexes but for later indexes, this is not working as expected.

StuartLC
  • 104,537
  • 17
  • 209
  • 285
Vipin
  • 27
  • 1
  • 5
  • The list was already sorted after your while loop. So remove the 'sort' and your list will be sorted. – Jesse de Wit Jun 17 '19 at 11:35
  • Can you show your desired result vs the actual result? – Sweeper Jun 17 '19 at 11:37
  • I have updated the actual scenario, please look at once. – Vipin Jun 17 '19 at 11:46
  • 1
    the better variant would be to have a datastructure (read: class) that you add to the list. and then when you need the string you call the (overloaded) ToString() method. – FalcoGer Jun 17 '19 at 11:47
  • in case of more than 10 disks, Desired Index value: 1,2,3,4,5,6,7,8,9,10,11 Actual Index value: 1,10,11,2,3,4,5,6,7,8,9 – Vipin Jun 17 '19 at 11:55
  • stringList.OrderBy(x=> Convert.ToInt32((x.Split(' ')[0]))); – Power Mouse Jun 17 '19 at 12:05
  • Possible duplicate of [c# linq orderby numbers that are string (and you cannot convert them to int)](https://stackoverflow.com/questions/6396378/c-sharp-linq-orderby-numbers-that-are-string-and-you-cannot-convert-them-to-int) – xdtTransform Jun 17 '19 at 12:11
  • May you [edit] your last comment into your question? It's more explicite than the question it self. I will also recommend reading [mcve], it's a great guideline for not forgetting things like that in a question. – xdtTransform Jun 17 '19 at 12:15

4 Answers4

8

Put your object into a class structure and work with that strong type as long as possible:

public class DiskInfo
{
    private int index = 0;
    private string model = String.Empty;
    private unsigned long size = 0;

    public int getIndex() { return index; }
    public string getModel() { return model; }
    public unsigned long getSize() { return size; }

    public DiskInfo(int index, string model, unsigned long size)
    {
        this.index = index;
        this.model = model;
        this.size = size;
    }

    public string ToString()
    {
        return string.Format("{0, -15} {1,-35} {2, -20}", index, model, size);
    }
}
// ...
List<DiskInfo> lst = new List<DiskInfo>();
foreach (ManagementObject disk in objectSearcher.Get() )
{
    lst.Add(new DiskInfo(
        disk.GetPropertyValue("Index"),
        disk.GetPropertyValue("Model"),
        diskSize
    ));
}

Adjust types as needed.
Then you can use simple linq to sort.

lst = lst.OrderBy(x => x.getIndex());

On top of that you get IDE support and compiler errors instead of trying to figure out why you get format exceptions, etc when mucking around with strings. If your input data is not of the correct data type, then cast it then and there.
For example, index gets passed as a string:

string strIdx = "15";
lst.Add(new DiskInfo(int.Parse(strIdx)), ...)
FalcoGer
  • 2,278
  • 1
  • 12
  • 34
1

It's not working after 10th index.

That is because List().Sort invoke string's comparison function.In string comparison "0" is less than "1", "1" is less than "11" and "12" is less than "2" etc.So it is not working after 10.

You can definition a sample comparison function as below:

 public static int Compare(string a, string b)
        {
            return int.Parse(a.Substring(0, 15)).CompareTo(int.Parse(b.Substring(0, 15)));
        }

and then invoke it in sort method:

stringList.Sort(Compare);

The prerequisite is that your format is satisfied that its first 15 characters can convert to an integer.

xiaokang
  • 98
  • 1
  • 8
0

You seem to be deliberately left aligning index your numbers, which will mean that the ascending string sorted sequence of 1 through 12 would would be 1, 11, 12, 2, 3, 4, ...

Since you have the index value during the creation of the string, it would be wasteful to again parse the number out of the string in order to sort it. It would be better to retain the index and the string separately in a suitable data structure, sort by the index, and then project out just the string.

Updated for OP's new Question

Creating a custom POCO class (with or without an IComparable implementation) seems overkill everytime you need to sort an enumerable of related data by one of its properties.

Instead, you can easily build up a sortable anon class, struct or tuple containing the sortable integer and the concatenated string, then sort, then project out just the string. Either way, OP's GetPropertyValue method appears to return (reflect) a weak type such as object or string - accepted answer wouldn't compile as it needs to cast index to an int.

Here's value tuple solution:

var tuples = new List<(int index, string str)>();

foreach (ManagementObject disk in objectSearcher.Get() )
{
    var indexValue = int.Parse(disk.GetPropertyValue("Index"));
    tuples.Add((indexValue, string.Format("{0, -15} {1,-35} {2, -20}",
                                indexValue,
                                disk.GetPropertyValue("Model"),
                                diskSize)));
}

// Sort by index, and project out the assembled string.
var myList = tuples
          .OrderBy(t => t.index)
          .Select(t => t.str)
          .ToList();

Original Answer, OP had a simple loop

What I've done below is to keep a Value tuple of the original string, and the parsed integer value of the first 15 digits.

Note that this will break if there are non-numeric characters in the first 15 characters of your string.

// Test Data
var strings = Enumerable.Range(0, 12)
  .Select(i => (string.Format("{0, -15} {1,-35} {2, -20}", i, "Model", "35GB")));

// Project out a tuple of (index, string)    
var indexedTuples = strings.Select(s => (idx: int.Parse(s.Substring(0, 15)), str: s));
var sorted = indexedTuples.OrderBy(t => t.idx)
                          .Select(t => t.str);
StuartLC
  • 104,537
  • 17
  • 209
  • 285
0

You are probably looking for the "logical sort order" seen in Windows Explorer. Below I have replaced the default string comparer with a comparer using that API: StrCmpLogicalW

class Program
{
    public sealed class NaturalStringComparer : IComparer<string>
    {
        [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
        public static extern int StrCmpLogicalW(string psz1, string psz2);

        public int Compare(string a, string b) => StrCmpLogicalW(a, b);
    }

    static void Main()
    {
        var stringList = new List<string>();

        var index = 0;
        while (index < 12)
        {
            stringList.Add($"{index,-15} {"Model",-35} {"35GB",-20}");
            index++;
        }

        stringList.Sort(new NaturalStringComparer());

        foreach (var s in stringList)
        {
            Console.WriteLine(s);
        }
    }
}
l33t
  • 18,692
  • 16
  • 103
  • 180
  • Don't you think it's a really big roundabout to cast to a string, then call some external library to sort the strings by natural order instead of just sorting by the integer you start with to begin with? – FalcoGer Jun 17 '19 at 12:01
  • I assumed the `stringList` was the actual test data from some data source. I.e. no integers to work with. – l33t Jun 17 '19 at 12:05