3

I have this code which just simply sorts IEnumerable of integers based on digit number in each of the integer.

var ints = new List<int>() { 66, 7, 9, -5, -22, 67, 122, -333, 555, -2 };
var ordered = ints.OrderBy(x =>
{
   x = Math.Abs(x);

   Console.WriteLine($"Getting length of {x}");

   int len = 0;
   while (x >= 1)
   {
      len++;
      x /= 10;
   }
   return len;
});

   Console.WriteLine("After OrderBy");
   Console.WriteLine("Fetching first item in ordered sequence");
   Console.WriteLine($"First item is {ordered.First()}");

   Console.WriteLine(string.Join(" ", ordered));

So, when the program hits line with fetching first item in ordered sequence in that moment the IEnumerable is getting sorted(I'm receiving output lines Getting lenght of XXX) because OrderBy was deferred and that't quite clear.

But why when program hits Console.WriteLine(string.Join(" ", ordered)); I'm again getting this output? Is IEnumerable sorted again? (isn't it already sorted?)

monoh_
  • 1,019
  • 3
  • 13
  • 15
  • I get that you want to keep integer to integer, but it would be much easier, and more straight forward, to just measure the length of the damn number by `x.ToString().Length` :) – Alex Dec 22 '15 at 01:01
  • Well, I see how dumb is this code, but it was quite late when I asked. I could just sort by `Math.Abs(x)` and I would reach almost the same. – monoh_ Dec 22 '15 at 11:16

2 Answers2

4

When you assign the value to ordered, they are not actually ordered. It's basically an instruction to order a list. IOrderedEnumerable<int>. So each time you try to access the first item, or any other item, it will convert this to an ordered list, to give you that item. The first time when you run .First(), and the second time when you run string.Join.
In both cases the program will create 2 distinct instances of an ordered list, use it up, then dispose of it by losing the reference (since you're not saving it).

Alex
  • 14,338
  • 5
  • 41
  • 59
0

If you need Ordered to be sorted only once, you need to call ToList() on it, in which case you will see the list result only once. Since it ordered.

var ints = new List<int>() { 66, 7, 9, -5, -22, 67, 122, -333, 555, -2 };
var ordered = ints.OrderBy(x =>
{
    x = Math.Abs(x);

    Console.WriteLine($"Getting length of {x}");

    int len = 0;
    while (x >= 1)
    {
        len++;
        x /= 10;
    }
    return len;
}).ToList();

Console.WriteLine("After OrderBy");
Console.WriteLine("Fetching first item in ordered sequence");
Console.WriteLine($"First item is {ordered.First()}");
Console.WriteLine(string.Join(" ", ordered));
loneshark99
  • 706
  • 5
  • 16
  • 1
    It's better to call `.ToArray()`, rather than `.ToList()`. It's slightly faster and can use half the memory. – Enigmativity Dec 22 '15 at 02:23
  • AtEnigmativity I am not so sure about that, http://stackoverflow.com/questions/1105990/is-it-better-to-call-tolist-or-toarray-in-linq-queries. either ways in this example the difference will be hardly tangible. – loneshark99 Dec 22 '15 at 06:15
  • Don't forget to use the `@` symbol rather than the letters `At` to send someone a notification. – Enigmativity Dec 22 '15 at 06:26
  • sorry that key is not working working on keyboard :(, will keep in mind next time to search for it and then copy paste. – loneshark99 Dec 22 '15 at 06:34
  • Having read your link I stand by what I said. The `.ToList()` and `.ToArray()` methods both use a doubling strategy to allocate storage, but the `.ToArray()` does so more efficiently. And finally when `.ToArray()` finished it copies the working array to a perfectly sized array (using lightning fast `Array.CopyTo`) allowing the working sized array to be GCed. `.ToList()` hangs on to the working array for ever. So a list with 1,025 elements will have a 2,048 element array backing it. – Enigmativity Dec 22 '15 at 06:41