Click here to Skip to main content
14,118,915 members
Rate this:
 
Please Sign up or sign in to vote.
See more:
The late coding challenge for today is straightforward.

Given a set of integers, arrange the integers so that the final integer thus formed is the largest number possible.

For example.

{ 1,5,67,9 } would be arranged to form 96751

The list may contain up to 50 numbers, and each number will be less than 256 and positive.

What I have tried:

To keep the problems suitably ambiguous.

Last week's winner was Graeme_Grant, mainly because he taught us all words we never knew. Send Sean your details and something (possibly) appropriate will wing it's way home.
Posted
Updated 23-Jan-17 20:37pm
v2
Comments
PIEBALDconsult 20-Jan-17 18:48pm
   
"To keep the problems suitably ambiguous."


1 == 1
5 == 101
9 == 1001
67 == 1000011

110110011000011
Graeme_Grant 20-Jan-17 19:43pm
   
Linq makes it simple...

var sorted = (new [] { 1, 5, 67, 9 }).Select(i => i.ToString()).OrderByDescending(i => i).Select(i => Convert.ToInt32(i)).ToArray();

but where is the fun in that... ;)
Graeme_Grant 20-Jan-17 21:54pm
   
It's a little bit tricker than this, so I've created a one-liner Linq solution[^] below.
Patrice T 21-Jan-17 1:31am
   
Idea: keeping spaces in answer help reading the order of numbers.
Graeme_Grant 21-Jan-17 1:48am
   
My output was as per Chris' request. Not hard to add spaces if needed. ;)
Patrice T 21-Jan-17 1:54am
   
was just an idea, not a complaint.
"Not hard to add spaces"
Yes but easier with spaces already there.
Graeme_Grant 21-Jan-17 2:21am
   
I work with raw data and specifications all day ... just a habit of conformity. Yes, you are right, spaces does make it easier to read - so data labelling the datasets. :)
Patrice T 21-Jan-17 2:58am
   
I think we can take some freedom on the statement.
remember that solutions giving a wrong circle have been accepted.
By the wan did you found why your translation didn't work?
Graeme_Grant 21-Jan-17 6:33am
   
Sorry, forgot about it. I will have a look at it again tonight.
Graeme_Grant 21-Jan-17 6:33am
   
I just posted Solution 5 - visualize the 3 different sorting algorithms in action. Manually coded sorting algorithms rather than letting Linq do all the heavy lifting. I left this one with the spaces rather than a joined string. ;)
PIEBALDconsult 21-Jan-17 13:47pm
   
Just told mine to accept "6.7E+1" as an integer... :D
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 7

System.Console.WriteLine ( BiggestIntegerInator
(
  System.Environment.CommandLine.Rive 
  ( Option.RemoveEmptyEntries 
  | Option.RemoveQuotes 
  | Option.HonorEscapes 
  | Option.HonorQuotes 
  )
) ) ;


(Rive is in one of my Articles, it's a more flexible version of Split.)

1 05 6.7E+1 "9" yields 96751


private static string
BiggestIntegerInator
(
  System.Collections.Generic.IList<string> Values
)
{
  System.Text.StringBuilder result = new System.Text.StringBuilder() ;

  System.Collections.Generic.SortedList<string,int> l =
    new System.Collections.Generic.SortedList<string,int>
    (
      Values.Count
    ,
      new DescendingComparer<string>()
    ) ;

  for ( int i = 0 ; i < Values.Count ; i++ )
  {
    string v = Values [ i ] ;

    double j ;

    if ( System.Double.TryParse ( v , out j ) && ( ( v = j.ToString() ).IndexOf ( '.' ) == -1 ) )
    {
      if ( l.ContainsKey ( v ) )
      {
        l [ v ]++ ;
      }
      else
      {
        l [ v ] = 1 ;
      }
    }
  }

  for ( int i = 0 ; i < l.Count ; i++ )
  {
    string v = l.Keys [ i ] ;

    for ( int j = 0 ; j < l [ v ] ; j++ )
    {
      result.Append ( v ) ;
    }
  }

  return  ( result.ToString() );
}

private class DescendingComparer<T> : System.Collections.Generic.IComparer<T>
where T : System.IComparable<T>
{
  public int
  Compare
  (
    T Op0
  ,
    T Op1
  )
  {
    return ( Op1.CompareTo ( Op0 ) ) ;
  }
}


OK, so that comparer above doesn't quite do the trick, here's another:

private class DescendingNumericStringComparer : System.Collections.Generic.IComparer<string>
{
  public int
  Compare
  (
    string Op0
  ,
    string Op1
  )
  {
    int result = 0 ;

    int i0 = -1 ;
    int i1 = -1 ;

    while ( ( result == 0 ) && ( ( i0 < Op0.Length - 1 ) || ( i1 < Op1.Length - 1 ) ) )
    {
      if ( i0 < Op0.Length - 1 ) i0++ ;
      if ( i1 < Op1.Length - 1 ) i1++ ;

      result = Op1 [ i1 ].CompareTo ( Op0 [ i0 ] ) ;
    }

    if ( result == 0 )
    {
      result = Op0.Length.CompareTo ( Op1.Length ) ;
    }

    return ( result ) ;
  }
}


43 432 435 433 yields 435 43 433 432


So that one has a flaw as well. Here's a dirty little fix (not recommended, due to string manipulation) while I work on a better implementation:

private class DescendingStringComparer : System.Collections.Generic.IComparer<string>
{
  public int
  Compare
  (
    string Op0
  ,
    string Op1
  )
  {
    int result = (Op1 + Op0).CompareTo ( Op0 + Op1 ) ;

    if ( result == 0 )
    {
      result = Op0.Length.CompareTo ( Op1.Length ) ;
    }

    return ( result ) ;
  }
}


24 242 243 yields 243 24 242

Fourth comparer, no string concatenation.
Get the longer of the two lengths and iterate.
When we run out of characters in one of the values, start using the characters at the start of the other value instead.

private class DescendingStringComparer : System.Collections.Generic.IComparer<string>
{
  public unsafe int
  Compare 
  (
    string Op0
  ,
    string Op1
  )
  {
    int result = 0 ;

    int len = Op0.Length > Op1.Length ? Op0.Length : Op1.Length ;

    for ( int i = 0 ; ( result == 0 ) && ( i < len ) ; i++ )
    {
      char c0 = i < Op0.Length ? Op0 [ i ] : Op1 [ i - Op0.Length ] ;
      char c1 = i < Op1.Length ? Op1 [ i ] : Op0 [ i - Op1.Length ] ;
              
      result = c1.CompareTo ( c0 ) ;
    }

    if ( result == 0 )
    {
      result = Op0.Length.CompareTo ( Op1.Length ) ;
    }

    return ( result ) ;
  }
}
   
v4
Comments
Patrice T 22-Jan-17 0:43am
   
Hu,
I don't see how you are padding small numbers.
How do you handle 4 45 46 43 and 43 432 435 433 ?
PIEBALDconsult 22-Jan-17 1:17am
   
I don't; why would I?
Hmmm... I think I see the problem...
Graeme_Grant 22-Jan-17 1:42am
   
I like the idea of a SortedList using a custom comparer - I did not think of that! :)

But ppolymorphe is right, try this set: {1, 5, 67, 94, 96, 91, 9}. Your output is: 96-94-91-9-67-5-1 but should yield: 9-96-94-91-67-5-1 - the latter is a larger number - 996...51 is a larger number than 969...51
Graeme_Grant 22-Jan-17 1:50am
   
I plugged this in and it corrected the output:
private class InvertedCodeProjectComparer<T> : IComparer<T> where T : IComparable<T>
{
    int IComparer<T>.Compare(T x, T y)
        => Convert.ToInt32(string.Join("", new[] { y, x })) -
           Convert.ToInt32(string.Join("", new[] { x, y }));
}
PIEBALDconsult 22-Jan-17 2:03am
   
I'm on it, just testing now...
Graeme_Grant 22-Jan-17 2:06am
   
Sorry, but you have another problem (using your comparer - any actually):

Input: {122, 151, 169, 139, 99, 245, 31, 67, 42, 61, 180, 52, 248, 200, 90, 223, 109, 51, 91, 98, 186, 191, 215, 70, 193, 179, 43, 84, 203, 228, 106, 246, 142, 150, 81, 145, 123, 229, 135, 120, 41, 40, 9, 234, 53, 156, 115, 24, 220, 233} || elements: 50

Output: 99 98 91 90 9 84 81 70 67 61 53 52 51 43 42 41 40 31 248 246 245 24 234 233 229 228 223 220 215 203 200 193 191 186 180 179 169 156 151 150 145 142 139 135 123 122 120 115 109 106 || elements:51

Spaced to make it easier to spot wally ;)
PIEBALDconsult 22-Jan-17 2:12am
   
New comparer produces:
9 99 98 91 90 84 81 70 67 61 53 52 51 43 42 41 40 31 248 246 245 24 234 233 229 228 223 220 215 203 200 193 191 186 180 179 169 156 151 150 145 142 139 135 123 122 120 115 109 106
Graeme_Grant 22-Jan-17 2:28am
   
Glad to see it working. :)
PIEBALDconsult 22-Jan-17 2:27am
   
And how do you figure 51 elements?
Graeme_Grant 22-Jan-17 2:34am
   
one random test had an extra 9 in it ...

{122, 151, 169, 139, 99, 245, 31, 67, 42, 61, 180, 52, 248, 200, 90, 223, 109, 51, 91, 98, 186, 191, 215, 70, 193, 179, 43, 84, 203, 228, 106, 246, 142, 150, 81, 145, 123, 229, 135, 120, 41, 40, 9, 234, 53, 156, 115, 24, 220, 233}

resulted in: 99999891908481706761535251434241403124824624524234233229228223220215203200193191186180179169156151150145142139135123122120115109106

On review, I can see in the above when I changed the source code to count, I didn't tell split to ignore empty results - sorry.

But there is something odd going on with that test...

PIEBALDconsult 22-Jan-17 2:41am
   
Ah, OK.
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 1

My Clipper (xBase family like FoxPro) language once again.
Procedure:
1) Convert to strings
2) Sort the numbers in reverse order after some padding to same size.

*   CCCP Code Challenge Code Project
*    Build largest integer
clear
largest({1, 5, 67, 9})
largest({100, 11, 10, 110, 112,1,114})
largest({4, 45, 46, 43})
largest({43, 432, 435, 433})
largest({2, 243, 242, 245, 241, 24, 221})
largest({20, 221, 226, 202, 2, 201})

procedure largest(lst)
	*	convert 2 string
	for scan=1 to len(lst)
		lst[scan]= str(lst[scan],,,.T.)
	next
	?
	? lst[1]
	for scan=2 to len(lst)
		?? " "
		?? lst[scan]
	next
		
	*	tri par insertion
	for scan=2 to len(lst)
		for ptr= scan to 2 step -1
			*	pad until same lentgh 
			mx= max(len(lst[ptr]), len(lst[ptr-1]))+1
			tmp1= lst[ptr]
			while len(tmp1) < mx
				tmp1 += lst[ptr]
			enddo
			tmp1= left(tmp1,mx)
			
			tmp2= lst[ptr-1]
			while len(tmp2) < mx
				tmp2 += lst[ptr-1]
			enddo
			tmp2= left(tmp2,mx)
			
			if tmp1 > tmp2
				tmp= lst[ptr]
				lst[ptr]= lst[ptr-1]
				lst[ptr-1]= tmp
			endif
		next
	next
	
	*	result
	? lst[1]
	for scan=2 to len(lst)
		?? " "
		?? lst[scan]
	next
	
	return

Variant without padding the strings:
for scan=2 to len(lst)
    for ptr= scan to 2 step -1
        if len(lst[ptr])= len(lst[ptr-1])
            tmp1= lst[ptr]
            tmp2= lst[ptr-1]
        else
            mx= len(lst[ptr])+ len(lst[ptr-1])- 1
            for scanl= 0 to mx
                tmp1= lst[ptr, scanl%len(lst[ptr])+1]
                tmp2= lst[ptr-1, scanl%len(lst[ptr-1])+1]
                if tmp1 != tmp2
                    exit
                endif
            next
        endif
        if tmp1 > tmp2
            tmp= lst[ptr]
            lst[ptr]= lst[ptr-1]
            lst[ptr-1]= tmp
        endif
    next
next

Test sets
1 5 67 9
9 67 5 1

100 11 10 110 112 1 114
114 112 11 1 110 10 100

4 45 46 43
46 45 4 43

43 432 435 433
435 43 433 432

2 243 242 245 241 24 221
245 243 24 242 241 2 221

20 221 226 202 2 201
226 2 221 202 20 201

Note: Spaces in answers just help reading out how numbers are ordered.

[Update] Refined the sort part
The padding part is really tricky, my best guess so far:
The padding is to be made until maximum length + 1
The padding is to be made by repeating the value.

Take 20 and 202. What is the order ?
padding: 20 -> 2020, 202 -> 2022
The order is same as for 2020 and 2022, so 202, 20.

Take 24 and 242. What is the order ?
padding: 24 -> 2424, 242 -> 2422
The order is same as for 2424 and 2422, so 24, 242.
   
v20
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 2

A LINQ one-liner! (Well, to be honest, about eight lines brutally mashed into one...)
public static BigInteger DoChallenge(this IEnumerable<byte> question)
{
    return (from x in question
            orderby (x < 10) ? x * 1110 :
                    (x < 100) ? x * 100 + Math.Min(x, (x % 10) * 10 + x / 10) :
                    x * 10 descending
            select new BigInteger(x))
        .Aggregate((x, y) =>
            (y < 10) ? x * 10 + y :
            (y < 100) ? x * 100 + y :
            x * 1000 + y);
}
The math idea is to convert each 1- 2- and 3-digit number to a 4-digit number for the purposes of sorting:
  • 123 goes to 1230
  • 12 goes to 1212, slotting it between 122 (1220) and 121 (1210)
  • 21 goes to 2112, slotting it between 212 (2120) and 211 (2110)
  • 2 goes to 2220, slotting it between 223 (2230) and 221 (2210), and between 23 (2323) and 21 (2112)
It seems to work for a bunch of assorted test cases, though it's not thoroughly tested or proven yet.
   
Comments
Graeme_Grant 20-Jan-17 20:30pm
   
I get a wrong result for this set: { 1, 5, 67, 94, 96, 91, 9 } - 910668943 instead of 99694916751
H2O-au 20-Jan-17 20:42pm
   
Yeah I get 910668943 when I use an int instead of a System.Numerics.BigInteger. The aggregation epic-fails. Seems that lowly ints can't handle 150-digit numbers! :D Though with BigInteger I get 99694916751.
Graeme_Grant 20-Jan-17 20:57pm
   
ah ... int vs BigInt ... gotcha! Will check that later... :)
Patrice T 20-Jan-17 20:46pm
   
I get: 9 96 94 91 67 5 1
Graeme_Grant 20-Jan-17 21:58pm
   
"Given a set of integers..." > The input should be of type int and not byte...
PIEBALDconsult 21-Jan-17 0:30am
   
Strings are easier...
Graeme_Grant 21-Jan-17 0:36am
   
I try to code to the challenge like it is a specification. Chris asked for Int[]/List<int> for input and a single number as a string for output. What we do in the middle is up to us. ;)
PIEBALDconsult 21-Jan-17 1:05am
   
"To keep the problems suitably ambiguous."

I would not be so restrictive; he did _not_ ask for Int[]/List<int> .
Graeme_Grant 21-Jan-17 1:10am
   
Looking forward to seeing what you come up with... :)
PIEBALDconsult 21-Jan-17 1:42am
   
Did you see my comment on the challenge?
Graeme_Grant 21-Jan-17 1:47am
   
Did you see that I too posted just below yours? ;)
Chris Maunder 21-Jan-17 4:13am
   
I said "a set of integers", not "a set of values of type int". I mean integers in the mathematical sense.
Graeme_Grant 21-Jan-17 6:10am
   
Bad habit of working with RFCs, specification documents, etc all day... Glad to know that your definitions are loose... :)
PIEBALDconsult 21-Jan-17 14:24pm
   
Oh shoot, a Set doesn't allow duplicates, my solution (so far) allows duplicates, but I can easily change that or make it configurable...
Chris Maunder 21-Jan-17 17:00pm
   
Since when does a set not allow duplicates? A set of marbles can contain two marbles that are the same.
PIEBALDconsult 21-Jan-17 17:44pm
   
A Set in the mathematical sense.

And no two marbles are actually the same.
H2O-au 21-Jan-17 17:14pm
   
Actually, my main reason for using byte is more devious. I know my algorithm fails for numbers above 999. I couldn't be bothered going through and checking that all numbers are less than 999, so by using byte, I cover the required range but make the data integrity the caller's responsibility! ;D

I'm sure I could justify it by mumbling something about 'type safety', but I'm probably just being lazy...
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 3

Here is another Linq one-liner. I've wrapped it up as an extension method. The comparer can be used for both ascending & descending calculation although for this challenge only descending positive numbers ranging from 1 to 256 is required.

The core logic is a simple one-liner that can be used with any sorting algorithm - join (not add) the numbers alternatively and subtract the first value from the second. A negative result means left is smaller (we need to swap), zero is equal (nothing to do), and positive is left is larger (nothing to do). eg: {94, 9} = 949 - 994 = -45, so we need to swap the numbers and the new sequence should be {9, 94}. However, {1, 112} = 1112 - 1121 = -9, so the correct order is {112, 1}.

Here is the comparer method that does all the work:
class CodeProjectComparer : IComparer<int>
    {
        int IComparer<int>.Compare(int x, int y)
            => Convert.ToInt32(string.Join("", new[] { x, y })) -
               Convert.ToInt32(string.Join("", new[] { y, x }));
    }

This class can be used with any sorting algorithm. I have added Solution 5 that visually demonstrates the effectiveness of the comparer method above with up to 9 different sorting algorithms.

If you do not want a seperate class but rather as an expression-body member[^]:
Comparer<int> Compare()
        => Comparer<int>.Create((x, y)
            => Convert.ToInt32(string.Join("", new[] { x, y })) -
               Convert.ToInt32(string.Join("", new[] { y, x })));

Here is a Linq one-liner (used in this solution) that includes the comparer in-line:
public static string ArrangedFormNumbersDescending(this int[] nums)
    => string.Join("", 
                   nums.OrderByDescending(
                       i => i, 
                       Comparer<int>.Create((x, y)
                           => Convert.ToInt32(string.Join("", new[] { x, y })) - 
                              Convert.ToInt32(string.Join("", new[] { y, x })))));

Side note: If you are interested in seeing how Microsoft implements sorting for their Linq OrderBy and OrderByDescending, you can view their source code[^].

Below is a sample app using the Linq one-liner and the output it generates.

Here is my attempt, of a fully working version, to make the solution "suitably ambiguous" to meet one of the challenge's requirement:
using System                                                                                      ;
using System.Collections.Generic                                                                  ;
using System.Linq                                                                                 ;

namespace ArrangedFormNumbers                                                                     {
    static class Program                                                                          {
        static void Main(string[] args)                                                           {
            var max = 50                                                                          ;
            var r = new Random()                                                                  ;
            var randomSet = new int[max]                                                          ;

            for (int i = 0; i < max; i++)                                                         {
                for (;;)                                                                          {
                    var n = r.Next(1, 255)                                                        ;
                    if (!randomSet.Contains(n))                                                   {
                        randomSet[i] = n                                                          ;
                        break                                                                     ;
                                                                                                  }
                                                                                                  }
                                                                                                  }
            var tests = new List<int[]>()                                                         {
                new[] { 1, 5, 67, 9 },
                new[] { 1, 5, 67, 94, 96, 91, 9 },
                new[] { 11, 112, 1, 114 },
                randomSet                                                                         }
                                                                                                  ;
            foreach (var test in tests)                                                           {
                Console.WriteLine($"Input:  {{{string.Join(", ", test)}}}")                       ;
                Console.WriteLine($"Output: {test.ArrangedFormNumbersDescending()}\r\n")          ;
                                                                                                  }
            Console.WriteLine("-- Press any key to exit --")                                      ;
            Console.ReadKey()                                                                     ;
                                                                                                  }
                                                                                                  }
    public static class ArrangeFormNumbersExtension                                               {
        public static string ArrangedFormNumbersAscending(this int[] nums)
            => string.Join("", nums.OrderBy(
                i => i,
                Comparer<int>.Create((x, y) =>
                    Convert.ToInt32(string.Join("", new[] { x, y })) - 
                    Convert.ToInt32(string.Join("", new[] { y, x })))))                           ;

        public static string ArrangedFormNumbersDescending(this int[] nums)
            => string.Join("", nums.OrderByDescending(
                i => i, 
                Comparer<int>.Create((x, y) => 
                    Convert.ToInt32(string.Join("", new[] { x, y })) - 
                    Convert.ToInt32(string.Join("", new[] { y, x })))))                           ;
                                                                                                  }
                                                                                                  }

This is a properly formatted version:
using System;
using System.Collections.Generic;
using System.Linq;

namespace ArrangedFormNumbers
{
    static class Program
    {
        static void Main(string[] args)
        {
            var max = 50;
            var r = new Random();
            var randomSet = new int[max];

            for (int i = 0; i < max; i++)
            { 
                for (;;)
                {
                    var n = r.Next(1, 255);
                    if (!randomSet.Contains(n))
                    {
                        randomSet[i] = n;
                        break;
                    }
                }
            }

            var tests = new List<int[]>()
            {
                new[] { 1, 5, 67, 9 },
                new[] { 1, 5, 67, 94, 96, 91, 9 },
                new[] { 11, 112, 1, 114 },
                randomSet
            };


            foreach (var test in tests)
                Console.WriteLine($"Input:  {{{string.Join(", ", test)}}}\r\nOutput: {test.ArrangedFormNumbersDescending()}\r\n");

            Console.WriteLine("-- Press any key to exit --");
            Console.ReadKey();
        }
    }

    public static class ArrangeFormNumbersExtension
    {
        public static string ArrangedFormNumbersAscending(this int[] nums)
            => string.Join("", nums.OrderBy(
                i => i,
                Comparer<int>.Create((x, y) =>
                    Convert.ToInt32(string.Join("", new[] { x, y })) - Convert.ToInt32(string.Join("", new[] { y, x })))));

        public static string ArrangedFormNumbersDescending(this int[] nums)
            => string.Join("", nums.OrderByDescending(
                i => i, 
                Comparer<int>.Create((x, y) => 
                    Convert.ToInt32(string.Join("", new[] { x, y })) - Convert.ToInt32(string.Join("", new[] { y, x })))));
    }
}

Update: Added a random generator to test range & max requirement.

Output:
Input:  {1, 5, 67, 9}
Output: 96751

Input:  {1, 5, 67, 94, 96, 91, 9}
Output: 99694916751

Input:  {11, 112, 1, 114}
Output: 114112111

Input:  {239, 196, 48, 208, 206, 94, 238, 246, 20, 81, 47, 115, 254, 144, 226, 45, 8, 249, 140, 53, 245, 190, 252, 135, 96, 63, 28, 110, 243, 143, 247, 146, 88, 13, 77, 18, 204, 142, 193, 34, 30, 148, 251, 228, 15, 170, 67, 149, 184, 202}
Output: 9694888817767635348474534302825425225124924724624524323923822822620820620420220196193190184181701514914814614414314214013513115110

-- Press any key to exit --

If you wanted to clean up the extensions and remove duplication, then you could use:
public static class ArrangeFormNumbersExtension
{
    private static Comparer<int> Compare()
        => Comparer<int>.Create((x, y)
            => Convert.ToInt32(string.Join("", new[] { x, y })) - Convert.ToInt32(string.Join("", new[] { y, x })));

    public static string ArrangedFormNumbersAscending(this int[] nums)
        => string.Join("", nums.OrderBy(i => i, Compare()));

    public static string ArrangedFormNumbersDescending(this int[] nums)
        => string.Join("", nums.OrderByDescending(i => i, Compare()));
}

... but then it's no longer a one liner. Where is the fun in that.

Update: PIEBALDConsult doesn't like the idea of using strings in the Comparer, so here is a pure Integer calculated version just for him ;)

1. Comparer class version:
class CodeProjectComparer : IComparer<int>
    {
        int IComparer<int>.Compare(int x, int y) => (int)(x.Concat(y) - y.Concat(x));
    }

2. expression-body member[^]:
Comparer<int> Compare()
        => Comparer<int>.Create((x, y) => (int)(x.Concat(y) - y.Concat(x));

3. A Linq one-liner (used in this solution) that includes the comparer in-line:
public static string ArrangedFormNumbersDescending(this int[] nums)
    => string.Join("", nums.OrderByDescending(
           i => i, Comparer<int>.Create((x, y) => (int)(x.Concat(y) - y.Concat(x)))));

All three (3) versions will however require the following supporting extension:
public static class SystemExtension
{
    public static Int64 Concat(this Int32 x, Int32 y)
    {
        Int64 pow = 1;
        while (pow <= y) pow = ((pow << 2) + pow) << 1;
        return x * pow + y;
    }
}

Here is the complete Integer only version:
using System;
using System.Collections.Generic;
using System.Linq;

namespace ArrangedFormNumbers
{
    public static class AppExtensions
    {
        public static string ArrangedFormNumbersDescending(this int[] nums)
            => string.Join(" ", nums.OrderByDescending( i => i, Comparer<int>.Create((x, y) => (int)(x.Concat(y) - y.Concat(x)))));

        public static Int64 Concat(this Int32 x, Int32 y)
        {
            Int64 pow = 1;
            while (pow <= y) pow = ((pow << 2) + pow) << 1;
            return x * pow + y;
        }
    }

    public static class Program
    {
        static void Main(string[] args)
        {
            var tests = InitTests();
            foreach (var test in tests)
                Console.WriteLine($"----\r\nInput:  {{{string.Join(", ", test)}}}\r\n\r\nOutput: {test.ArrangedFormNumbersDescending()}\r\n");

            Console.WriteLine("----\r\n\r\n-- Press any key to exit --");
            Console.ReadKey();
        }

        static List<int[]> InitTests(int max = 50)
        {
            var r = new Random();
            var randomSet = new int[max];

            for (int i = 0; i < max; i++)
                for (;;)
                {
                    var n = r.Next(1, 255);
                    if (!randomSet.Contains(n))
                    {
                        randomSet[i] = n;
                        break;
                    }
                }

            return new List<int[]>()
            {
                new[] { 1, 5, 67, 9 },
                new[] { 1, 5, 67, 94, 96, 91, 9 },
                new[] { 11, 112, 1, 114 },
                new[] {4, 45, 46, 43},
                new[] {23, 232, 235, 233},
                new[] { 102, 1, 36, 12, 21, 79, 170, 9 },
                new[] { 3, 12, 306, 12, 21, 190, 170, 90 },
                new[] { 122, 151, 169, 139, 99, 245, 31, 67, 42, 61, 180, 52, 248, 200, 90, 223, 109, 51, 91, 98, 186, 191, 215, 70, 193, 179, 43, 84, 203, 228, 106, 246, 142, 150, 81, 145, 123, 229, 135, 120, 41, 40, 9, 234, 53, 156, 115, 24, 220, 233 },
                randomSet
            };
        }
    }
}

And the output (results spaced as suggested by ppolymorphe) using the new (pure Integer) comparer:
----
Input:  {1, 5, 67, 9}

Output: 9 67 5 1

----
Input:  {1, 5, 67, 94, 96, 91, 9}

Output: 9 96 94 91 67 5 1

----
Input:  {11, 112, 1, 114}

Output: 114 112 11 1

----
Input:  {4, 45, 46, 43}

Output: 46 45 4 43

----
Input:  {23, 232, 235, 233}

Output: 235 233 23 232

----
Input:  {102, 1, 36, 12, 21, 79, 170, 9}

Output: 9 79 36 21 170 12 1 102

----
Input:  {3, 12, 306, 12, 21, 190, 170, 90}

Output: 90 3 306 21 190 170 12 12

----
Input:  {122, 151, 169, 139, 99, 245, 31, 67, 42, 61, 180, 52, 248, 200, 90, 223, 109, 51, 91, 98, 186, 191, 215, 70, 193, 179, 43, 84, 203, 228, 106, 246, 142, 150, 81, 145, 123, 229, 135, 120, 41, 40, 9, 234, 53, 156, 115, 24, 220, 233}

Output: 99 9 98 91 90 84 81 70 67 61 53 52 51 43 42 41 40 31 248 246 245 24 234 233 229 228 223 220 215 203 200 193 191 186 180 179 169 156 151 150 145 142 139 135 123 122 120 115 109 106

----
Input:  {241, 17, 122, 59, 108, 206, 69, 107, 64, 140, 38, 84, 3, 249, 10, 133, 240, 253, 54, 248, 182, 51, 247, 11, 132, 172, 61, 171, 175, 219, 13, 66, 35, 123, 252, 99, 195, 147, 119, 250, 204, 161, 232, 77, 106, 162, 146, 93, 168, 131}

Output: 99 93 84 77 69 66 64 61 59 54 51 38 35 3 253 252 250 249 248 247 241 240 232 219 206 204 195 182 175 172 17 171 168 162 161 147 146 140 133 132 13 131 123 122 119 11 108 107 106 10

----

-- Press any key to exit --

Enjoy!
   
v17
Comments
PIEBALDconsult 22-Jan-17 12:26pm
   
"but then it's no longer a one liner. Where is the fun in that"
You can have either one-line or performance; you can't have both.
Graeme_Grant 22-Jan-17 12:45pm
   
Clean or duplicated code... yes...
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 4

Updated on 1/21. Obviously the previous solution has a flaw in it. Thanks to Graeme_Grant for pointing it out. I modified the solution to add some redundant step into it such as grouping by the first digit and then sort it by group. After that concatenate the group into a string. Anyway, I think the key here is the Comparer<int>. I think the solution from Graeme_Grant is outstanding.

------
i think this should do it. First, divide the integer by base 10 to the power of length of the integer (weight), then sort it by the weight and concatenate the output into string using string.join method.

logic
var testData = new List<int[]>()
            {
                new[] { 1, 5, 67, 6, 9, 91 },
                new[] { 81, 8, 8111, 811 },
                new[] { 1,102,0,10,1, 60, 61 },
                new[] { 2,0,11,10,69,79,4 },
                new[] { 102,01,36,12,21,79,970,9 },
                new[] { 3,012,306,12,21,790,970,90 },
                new[] { 10, 5, 67, 97, 8 },
                new[] { 70, 71, 102, 1, 10,8,80, 9,91, 90, 900, 9000, 2, 4, 1112, 1100,11,99}
            };

            foreach (var d in testData)
            {
                var result = string.Join("", d.GroupBy(x => x.ToString().Substring(0, 1), (number, subList) => new
                {
                    Number = number,
                    SubList = subList.OrderByDescending(x => x, 
                    Comparer<int>.Create((a, b) => int.Parse(string.Format("{0}{1}", a, b)) - int.Parse(string.Format("{0}{1}", b, a))))

                }).OrderByDescending(x => x.Number).SelectMany(s => s.SubList));

                Console.WriteLine("input: {0} ", string.Join(",", d));
                Console.WriteLine("output: {0} ", result);
                Console.WriteLine();
            }

            Console.WriteLine();
            Console.ReadLine();


Output
input: 1,5,67,6,9,91
output: 9-91-67-6-5-1

input: 81,8,8111,811
output: 8-81-811-8111

input: 1,102,0,10,1,60,61
output: 61-60-1-1-102-10-0

input: 2,0,11,10,69,79,4
output: 79-69-4-2-11-10-0

input: 102,1,36,12,21,79,970,9
output: 9-970-79-36-21-12-1-102

input: 3,12,306,12,21,790,970,90
output: 970-90-790-3-306-21-12-12

input: 10,5,67,97,8
output: 97-8-67-5-10

input: 70,71,102,1,10,8,80,9,91,90,900,9000,2,4,1112,1100,11,99
output: 9-99-91-90-900-9000-8-80-71-70-4-2-1112-1-11-1100-102-10
   
v2
Comments
Graeme_Grant 20-Jan-17 23:33pm
   
try this set: { 1, 5, 67, 94, 96, 91, 9 }
Bryian Tan 21-Jan-17 0:12am
   
haha, yeah, too good to be true, doesn't work well with 81,8,91,9
Graeme_Grant 21-Jan-17 0:24am
   
yes ... I too made the mistake with my initial comment at the top... [edit] I gather that is why Chris posted it as a challenge. ;)
Graeme_Grant 22-Jan-17 0:32am
   
Hey Bryian, Thanks for the comments in your solution. I am humbly honored. :)
Patrice T 21-Jan-17 1:29am
   
I get
102 1 36 12 21 79 970 9
9 970 79 36 21 12 1 102

3 12 306 12 21 790 970 90
970 90 790 3 306 21 12 12
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 5

[Updated] Using the IComparer<T> method from Solution 3, here is a visualization of the sorting with up to nine (9) sorting algorithms: Bubble sort[^], Comb Sort[^], Cycle Sort[^], Gnome Sort[^], Insertion Sort[^], Odd-even sort[^], Quick Sort[^], Selection Sort[^], Shell sort[^].

I have made them asynchronous so several can run simultaneously and see how they function. I've colour-coded the actions: blue = evaluating, green = swapping, gray = inactive. Lastly, there is a sleep delay value that can be set before running the app so that you can adjust it to suit you.

Here is my attempt, a fully working version, to make the solution "suitably ambiguous" to meet one of the challenge's requirement:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace VisualSort {

public class CodeProjectComparer : IComparer<int> { int IComparer<int>.Compare(int a, int b) => Convert.ToInt32(string.Join("", new[] { a, b })) - Convert.ToInt32(string.Join("", new[] { b, a })); }

public enum SortType { Bubble, Comb, Cycle, Gnome, Insertion, OddEven, Quick, Selection, Shell }

static class Program { static object P = new object(); static int Q { get; set; } static int R { get; set; } static Dictionary<SortType, SortTracker> S = new Dictionary<SortType, SortTracker>(); static List<List<Position>> U = new List<List<Position>>();
static void Main(string[] a) { ucolr = Console.ForegroundColor; ecolr = ConsoleColor.Blue; scolr = ConsoleColor.Green; R = 50; Q = 100; Intro(); PromptUser("-- Press any key to begin --", true); MainAsync().GetAwaiter().GetResult(); PromptUser("-- Press any key to exit --"); }
static async Task MainAsync() { InitPos(); Add(SortType.Bubble, "Bubble Sort Descending"); Add(SortType.Insertion, "Insertion Sort"); Add(SortType.Quick, "Quick Sort"); Add(SortType.Shell, "Shell Sort Descending"); InitArea(); var a = InitTests(); for (int b = 0; b < a.Count; b++) { await ExecuteTestAsync(a[b], string.Format("Running Test: {0} of {1}...", b + 1, a.Count)); if (b < a.Count - 1) PromptUser("-- Press any key to run the next test --"); } }
static async Task ExecuteTestAsync(int[] a, string b) { ClearArea(S.Count); SetAreas(a); await Task.Delay(1000); WriteAt(new Position(5, upos), ucolr, b); await Task.WhenAll(S.Execute(a)); }
static void Add(SortType a, string b) { S.Add(a, new SortTracker(b, ucolr, ecolr, scolr, U[S.Count], new Position(23, 9 + S.Count * 10), Q, WriteAt)); }
static List<int[]> InitTests(int a = 50) { R = a; var b = new Random(); var c = new int[R]; for (int d = 0; d < R; d++) for (;;) { int e = b.Next(1, 255); if (!c.Contains(e)) { c[d] = e; break; } } return new List<int[]>(){new[]{1,5,67,9},new[]{1,5,67,94,96,91,9},new[]{11,112,1,114},new[]{4,45,46,43},new[]{23,232,235,233},new[]{102,1,36,12,21,79,170,9},new[]{3,12,306,12,21,190,170,90},c}; }
static int upos; static ConsoleColor ucolr { get; set; } static ConsoleColor ecolr { get; set; } static ConsoleColor scolr { get; set; }
static void SetAreas(int[] a) { foreach (var tracker in S) for (int b = 0; b < a.Length; b++) WriteAt(tracker.Value.Z[b], tracker.Value.W, a[b].ToString().PadLeft(5)); }
static void ClearArea(int a) { int b = 9; foreach (var tracker in S) { for (int c = 0; c < R; c++) WriteAt(tracker.Value.Z[c], tracker.Value.W, "     "); WriteAt(new Position(23, b), tracker.Value.W, "0".PadRight(5)); b += 10; } WriteAt(new Position(5, upos), Console.ForegroundColor, "".PadLeft(40)); }
static void Intro() { Console.WriteLine("CodeProject's Coding challenge: arrange numbers to form the largest possible integer"); Console.WriteLine(""); Console.WriteLine("This is a Visualualisaton of different sorting algorithms on number datasets\r\nthat meet the following requirements:"); Console.WriteLine(""); Console.WriteLine("1. Given a set of integers, arrange the integers so that the final integer\r\n   thus formed is the largest number possible."); Console.WriteLine("2. The list may contain up to 50 numbers, and each number will be less than\r\n   256 and positive."); Console.WriteLine(""); Console.WriteLine("There are a number of different sorting algorithms that can be toggled on/off\r\nin the MainAsync method before running."); Console.WriteLine(""); Console.WriteLine("NOTE: It is advisable to adjust the size of the console window to see all\r\nsorts in action at the same time."); Console.WriteLine(""); Console.WriteLine("Enjoy! :)"); Console.WriteLine(""); Console.WriteLine(""); }
static void InitPos() { int a = R < 11 ? R : 10; int b = R < 11 ? 1 : R / 10; for (int c = 0; c < 9; c++) U.Add(new List<Position>()); for (int d = 0; d < b; d++) for (int e = 0; e < a; e++) { int f = 3; foreach (var postion in U) { postion.Add(new Position((e * 5) + 5, d + f)); f += 10; } } }
static void InitArea() { upos = 2 + S.Count * 10; Console.CursorVisible = false; Console.Clear(); var a = Console.ForegroundColor; var b = Console.BackgroundColor; int c = 1, d = 9; foreach (var tracker in S) { Console.BackgroundColor = ConsoleColor.Blue; WriteAt(new Position(5, c), ConsoleColor.White, string.Format("  {0}", tracker.Value.P).PadRight(50)); Console.BackgroundColor = b; WriteAt(new Position(15, d), ConsoleColor.White, "Steps:"); c += 10; d += 10; } ConsoleBox(new Position(60, 3), 17, 8, ConsoleColor.White); Console.BackgroundColor = ConsoleColor.Blue; WriteAt(new Position(61, 4), ConsoleColor.White, " COLOUR LEGEND "); Console.BackgroundColor = b; WriteAt(new Position(64, 6), ucolr, "idle"); WriteAt(new Position(64, 7), ecolr, "Evaluating"); WriteAt(new Position(64, 8), scolr, "Changing"); Console.ForegroundColor = a; }
static void WriteAt(Position a, ConsoleColor b, string c) { lock (P) { Console.ForegroundColor = b; if (a != null) Console.SetCursorPosition(a.C, a.R); Console.Write(c); } }
static void PromptUser(string a, bool b = false) { WriteAt(b ? null : new Position(5, upos), Console.ForegroundColor, a); Console.ReadKey(); }
static void ConsoleBox(Position a, int b, int c, ConsoleColor d) { var e = Console.ForegroundColor; Console.OutputEncoding = Encoding.GetEncoding(866); Console.ForegroundColor = d; int f = b - 2; var g = string.Join("", Enumerable.Range(1, f).Select(h => "─")); var i = new StringBuilder().Append("┌").Append(g).Append("┐\n").ToString(); var j = new StringBuilder().Append("│").Append("".PadLeft(f)).Append("│\n").ToString(); var k = new StringBuilder().Append("└").Append(g).Append("┘").ToString(); Console.SetCursorPosition(a.C, a.R); Console.Write(i); for (int l = 1; l < c; l++) { Console.SetCursorPosition(a.C, a.R + l); Console.Write(j); } Console.SetCursorPosition(a.C, a.R + c - 1); Console.Write(k); Console.ForegroundColor = e; } }

public enum FeedbackType { Eval, Swap, Done }

public class Position { public Position(int c, int r) { C = c; R = r; } public int C { get; set; } public int R { get; set; } }

public class SortTracker { public SortTracker() { }
public SortTracker(string a, ConsoleColor b, ConsoleColor c, ConsoleColor d, List<Position> e, Position f, int g, Action<Position, ConsoleColor, string> h) { P = a; W = b; X = c; Y = d; Z = e; V = g; U = f; Writer = h; S = new[] { -1, -1 }; }
public string P { get; set; } public int Q { get; set; } public int[] R { get; set; } int[] S { get; } Position U; public int V { get; set; } public ConsoleColor W { get; set; } public ConsoleColor X { get; set; } public ConsoleColor Y { get; set; } public List<Position> Z { get; set; } public Action<Position, ConsoleColor, string> Writer { get; set; }
public void a0(int[] a) { R = a; Q = 0; S[0] = -1; S[1] = -1; }
public Task a1(int a, int b, FeedbackType c, bool d) { switch (c) { case FeedbackType.Done: case FeedbackType.Eval: if (S[0] != -1) { Writer(Z[S[0]], W, R[S[0]].ToString().PadLeft(5)); Writer(Z[S[1]], W, R[S[1]].ToString().PadLeft(5)); } if (a != -1) { S[0] = a; S[1] = b; Writer(Z[a], X, R[a].ToString().PadLeft(5)); Writer(Z[b], X, R[b].ToString().PadLeft(5)); } break; case FeedbackType.Swap: if (d) { Writer(Z[S[0]], W, R[S[0]].ToString().PadLeft(5)); Writer(Z[S[1]], W, R[S[1]].ToString().PadLeft(5)); S[0] = a; S[1] = b; } Writer(Z[a], Y, R[a].ToString().PadLeft(5)); Writer(Z[b], Y, R[b].ToString().PadLeft(5)); break; } Writer(U, W, (++Q).ToString("N0")); return Task.Delay(V); } }

public static class SortHelpers { public static Task[] Execute(this Dictionary<SortType, SortTracker> a, int[] b) { var c = new Task[a.Count]; for (int d = 0; d < a.Count; d++) { var e = a.ElementAt(d); e.Value.a0(b.Select(f => f).ToArray()); switch (e.Key) { case SortType.Bubble: c[d] = e.Value.R.Bubble(new CodeProjectComparer(), e.Value.a1); break; case SortType.Comb: c[d] = e.Value.R.Comb(new CodeProjectComparer(), e.Value.a1); break; case SortType.Cycle: c[d] = e.Value.R.Cycle(new CodeProjectComparer(), e.Value.a1); break; case SortType.Gnome: c[d] = e.Value.R.Gnome(new CodeProjectComparer(), e.Value.a1); break; case SortType.Insertion: c[d] = e.Value.R.Insertion(new CodeProjectComparer(), e.Value.a1); break; case SortType.OddEven: c[d] = e.Value.R.OddEven(new CodeProjectComparer(), e.Value.a1); break; case SortType.Quick: c[d] = e.Value.R.Quick(new CodeProjectComparer(), e.Value.a1); break; case SortType.Selection: c[d] = e.Value.R.Selection(new CodeProjectComparer(), e.Value.a1); break; case SortType.Shell: c[d] = e.Value.R.Shell(new CodeProjectComparer(), e.Value.a1); break; } } return c; }
public static Task Bubble<T>(this IList<T> a, IComparer<T> b, Func<int, int, FeedbackType, bool, Task> c = null) => Task.Run(async () => { for (int d = 0; d < a.Count - 1; d++) for (int e = 1; e < a.Count - d; e++) { if (c != null) await c.Invoke(e, e - 1, FeedbackType.Eval, false); if (b.Compare(a[e], a[e - 1]) > 0) { a.P(e - 1, e); if (c != null) await c.Invoke(e - 1, e, FeedbackType.Swap, false); } } if (c != null) await c.Invoke(-1, -1, FeedbackType.Done, false); });
public static Task Comb<T>(this IList<T> a, IComparer<T> b, Func<int, int, FeedbackType, bool, Task> c = null) => Task.Run(async () => { double d = a.Count; var e = true; while (d > 1 || e) { d /= 1.247330950103979; if (d < 1) d = 1; int f = 0; e = false; while (f + d < a.Count) { int g = f + (int)d; if (c != null) await c.Invoke(f, g, FeedbackType.Eval, false); if (b.Compare(a[f], a[g]) < 0) { a.P(f, g); e = true; if (c != null) await c.Invoke(f, g, FeedbackType.Swap, false); } f++; } } if (c != null) await c.Invoke(-1, -1, FeedbackType.Done, false); });
public static Task Cycle<T>(this IList<T> a, IComparer<T> b, Func<int, int, FeedbackType, bool, Task> c = null) => Task.Run(async () => { for (int d = 0; d < a.Count; d++) { T e = a[d]; int f = d; do { int g = 0; for (int h = 0; h < a.Count; h++) { if (c != null) await c.Invoke(f, h, FeedbackType.Eval, false); if (h != d && b.Compare(a[h], e) > 0) g++; } if (f != g) { while (f != g && b.Compare(e, a[g]) == 0) { if (c != null) await c.Invoke(f, g, FeedbackType.Eval, false); g++; } T i = a[g]; a[g] = e; e = i; if (c != null) await c.Invoke(f, g, FeedbackType.Swap, true); f = g; } } while (f != d); } if (c != null) await c.Invoke(-1, -1, FeedbackType.Done, false); });
public static Task Gnome<T>(this IList<T> a, IComparer<T> b, Func<int, int, FeedbackType, bool, Task> c = null) => Task.Run(async () => { int d = 1; while (d < a.Count) { if (c != null) await c.Invoke(d, d - 1, FeedbackType.Eval, false); if (b.Compare(a[d], a[d - 1]) <= 0) d++; else { a.P(d, d - 1); if (c != null) await c.Invoke(d, d - 1, FeedbackType.Swap, false); if (d > 1) d--; } } if (c != null) await c.Invoke(-1, -1, FeedbackType.Done, false); });
public static Task Insertion<T>(this IList<T> a, IComparer<T> b, Func<int, int, FeedbackType, bool, Task> c = null) => Task.Run(async () => { int d; for (int e = 1; e < a.Count; e++) { T f = a[e]; d = e - 1; if (c != null) await c.Invoke(e, d, FeedbackType.Eval, false); while ((d >= 0) && (b.Compare(a[d], f) < 0)) { a[d + 1] = a[d]; if (c != null) await c.Invoke(d + 1, e, FeedbackType.Swap, true); d--; } a[d + 1] = f; if (c != null) await c.Invoke(d + 1, e, FeedbackType.Swap, true); } if (c != null) await c.Invoke(-1, -1, FeedbackType.Done, false); });
public static Task OddEven<T>(this IList<T> a, IComparer<T> b, Func<int, int, FeedbackType, bool, Task> c = null) => Task.Run(async () => { var d = false; while (!d) { d = true; for (int e = 1; e < a.Count - 1; e += 2) { if (c != null) await c.Invoke(e, e + 1, FeedbackType.Eval, false); if (b.Compare(a[e], a[e + 1]) < 0) { a.P(e, e + 1); d = false; if (c != null) await c.Invoke(e, e + 1, FeedbackType.Swap, false); } } for (int e = 0; e < a.Count - 1; e += 2) { if (c != null) await c.Invoke(e, e + 1, FeedbackType.Eval, false); if (b.Compare(a[e], a[e + 1]) < 0) { a.P(e, e + 1); d = false; if (c != null) await c.Invoke(e, e + 1, FeedbackType.Swap, false); } } } if (c != null) await c.Invoke(-1, -1, FeedbackType.Done, false); });
public static Task Quick<T>(this IList<T> a, IComparer<T> b, Func<int, int, FeedbackType, bool, Task> c = null) => Task.Run(async () => { await a.X(0, a.Count - 1, b, c); if (c != null) await c.Invoke(-1, -1, FeedbackType.Done, false); });
static async Task X<T>(this IList<T> a, int b, int c, IComparer<T> d, Func<int, int, FeedbackType, bool, Task> e = null) { if (b < c) { int f = await a.Y(b, c, d, e); await a.X(b, f - 1, d, e); await a.X(f + 1, c, d, e); } }
static async Task<int> Y<T>(this IList<T> a, int b, int c, IComparer<T> d, Func<int, int, FeedbackType, bool, Task> e = null) { int f, g; g = c; T h = a[g]; f = b; for (int i = b; i <= (c - 1); i++) { if (e != null) await e.Invoke(i, g, FeedbackType.Eval, false); if (d.Compare(a[i], h) >= 0) { a.P(i, f); if (e != null) await e.Invoke(i, f, FeedbackType.Swap, true); f++; } } a.P(f, g); if (e != null) await e.Invoke(f, g, FeedbackType.Swap, true); return f; }
public static Task Selection<T>(this IList<T> a, IComparer<T> b, Func<int, int, FeedbackType, bool, Task> c = null) => Task.Run(async () => { for (int d = a.Count - 1; d > 0; d--) { int e = d; for (int f = 0; f <= d; f++) { if (c != null) await c.Invoke(f, e, FeedbackType.Eval, false); if (b.Compare(a[f], a[e]) < 0) e = f; } a.P(d, e); if (c != null) await c.Invoke(d, e, FeedbackType.Swap, false); } if (c != null) await c.Invoke(-1, -1, FeedbackType.Done, false); });
public static Task Shell<T>(this IList<T> a, IComparer<T> b, Func<int, int, FeedbackType, bool, Task> c = null) => Task.Run(async () => { var d = true; int e = a.Count; while (d || (e > 1)) { d = false; e = (e + 1) / 2; for (int f = 0; f < (a.Count - e); f++) { if (c != null) await c.Invoke(f + e, f, FeedbackType.Eval, false); if (b.Compare(a[f + e], a[f]) > 0) { a.P(f + e, f); d = true; if (c != null) await c.Invoke(f + e, f, FeedbackType.Swap, false); } } } if (c != null) await c.Invoke(-1, -1, FeedbackType.Done, false); }); }

public static class CollectionExtensions { public static void P<T>(this IList<T> a, int b, int c) { if (a.Count < 2 || b == c) return; T d = a[b]; a[b] = a[c]; a[c] = d; } } }

This is a readable version for those who want to be able to read and understand it:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace VisualSort
{
    public class CodeProjectComparer : IComparer<int>
    {
        int IComparer<int>.Compare(int x, int y)
            => Convert.ToInt32(string.Join("", new[] { x, y })) - Convert.ToInt32(string.Join("", new[] { y, x }));
    }

    public enum SortType
    {
        Bubble,
        Comb,
        Cycle,
        Gnome,
        Insertion,
        OddEven,
        Quick,
        Selection,
        Shell
    }

    static class Program
    {
        static object lockObj = new object();
        static int SleepMS { get; set; }
        static int MaxEntries { get; set; } = 50;

        static Dictionary<SortType, SortTracker> Trackers = new Dictionary<SortType, SortTracker>();
        static List<List<Position>> Positions = new List<List<Position>>();

        static void Main(string[] args)
        {
            SleepMS = 100;
            IntroMessage();
            PromptUser("-- Press any key to begin --", true);
            MainAsync().GetAwaiter().GetResult();
            PromptUser("-- Press any key to exit --");
        }

        static async Task MainAsync()
        {
            InitPositions();

            //NOTE: Uncomment up to 4 different sorts at a time...
            //      or all 9 if you have the screen resolution
            //      (or set a very small console font size)
            //
            AddTracker(SortType.Bubble, "Bubble Sort Descending");
            //AddTracker(SortType.Comb, "Comb Sort Descending");
            //AddTracker(SortType.Cycle, "Cycle Sort Descending");
            //AddTracker(SortType.Gnome, "Gnome Sort Descending");
            AddTracker(SortType.Insertion, "Insertion Sort");
            //AddTracker(SortType.OddEven, "Odd-Even Sort Descending");
            AddTracker(SortType.Quick, "Quick Sort");
            //AddTracker(SortType.Selection, "Selection Sort Descending");
            AddTracker(SortType.Shell, "Shell Sort Descending");

            InitWorkArea();
            var tests = InitTests();

            for (int i = 0; i < tests.Count; i++)
            {
                await ExecuteTestAsync(tests[i], $"Running Test: {i + 1} of {tests.Count}...");
                if (i < tests.Count - 1)
                    PromptUser("-- Press any key to run the next test --");
            }
        }

        static async Task ExecuteTestAsync(int[] test, string msg)
        {
            ClearAreas(Trackers.Count);
            SetAreas(test);
            await Task.Delay(1000);
            WriteAt(new Position(5, promptPosition), unsetColor, msg);
            await Task.WhenAll(Trackers.Execute(test));
        }

        static void AddTracker(SortType type, string Title)
        {
            Trackers.Add(type, new SortTracker(Title, unsetColor, evalColor, swapColor, 
                Positions[Trackers.Count], new Position(23, 9 + Trackers.Count * 10), SleepMS, WriteAt));
        }

        static List<int[]> InitTests(int max = 50)
        {
            MaxEntries = max;
            var r = new Random();
            var randomSet = new int[MaxEntries];

            for (int i = 0; i < MaxEntries; i++)
            {
                for (;;)
                {
                    var n = r.Next(1, 255);
                    if (!randomSet.Contains(n))
                    {
                        randomSet[i] = n;
                        break;
                    }
                }
            }

            return new List<int[]>()
            {
                new[] { 1, 5, 67, 9 },
                new[] { 1, 5, 67, 94, 96, 91, 9 },
                new[] { 11, 112, 1, 114 },
                new[] {4, 45, 46, 43},
                new[] {23, 232, 235, 233},
                new[] { 102, 1, 36, 12, 21, 79, 170, 9 },
                new[] { 3, 12, 306, 12, 21, 190, 170, 90 },
                randomSet
            };
        }

        #region Console

        static int promptPosition;

        static ConsoleColor unsetColor { get; } = Console.ForegroundColor;
        static ConsoleColor evalColor { get; } = ConsoleColor.Blue;
        static ConsoleColor swapColor { get; } = ConsoleColor.Green;

        const int Unset = -1;

        static void SetAreas(int[] test)
        {
            foreach (var tracker in Trackers)
                for (int i = 0; i < test.Length; i++)
                    WriteAt(tracker.Value.Positions[i], tracker.Value.UnsetColor, test[i].ToString().PadLeft(5));
        }

        static void ClearAreas(int count)
        {
            int yPos = 9;
            foreach (var tracker in Trackers)
            {
                for (int i = 0; i < MaxEntries; i++)
                    WriteAt(tracker.Value.Positions[i], tracker.Value.UnsetColor, "     ");
                WriteAt(new Position(23, yPos), tracker.Value.UnsetColor, "0".PadRight(5));
                yPos += 10;
            }
            WriteAt(new Position(5, promptPosition), Console.ForegroundColor, "".PadLeft(40));
        }

        static void IntroMessage()
        {
            Console.WriteLine("CodeProject's Coding challenge: arrange numbers to form the largest possible integer");
            Console.WriteLine("");
            Console.WriteLine("This is a Visualualisaton of different sorting algorithms on number datasets\r\nthat meet the following requirements:");
            Console.WriteLine("");
            Console.WriteLine("1. Given a set of integers, arrange the integers so that the final integer\r\n   thus formed is the largest number possible.");
            Console.WriteLine("2. The list may contain up to 50 numbers, and each number will be less than\r\n   256 and positive.");
            Console.WriteLine("");
            Console.WriteLine("There are a number of different sorting algorithms that can be toggled on/off\r\nin the MainAsync method before running.");
            Console.WriteLine("");
            Console.WriteLine("NOTE: It is advisable to adjust the size of the console window to see all\r\nsorts in action at the same time.");
            Console.WriteLine("");
            Console.WriteLine("Enjoy! :)");
            Console.WriteLine("");
            Console.WriteLine("");
        }

        static void InitPositions()
        {
            var cols = MaxEntries < 11 ? MaxEntries : 10;
            var rows = MaxEntries < 11 ? 1 : MaxEntries / 10;

            for (int i = 0; i < 9; i++)
                Positions.Add(new List<Position>());

            for (int y = 0; y < rows; y++)
            {
                for (int x = 0; x < cols; x++)
                {
                    var yOffset = 3;
                    foreach (var postion in Positions)
                    {
                        postion.Add(new Position((x * 5) + 5, y + yOffset));
                        yOffset += 10;
                    }
                }
            }
        }

        static void InitWorkArea()
        {
            promptPosition = 2 + Trackers.Count * 10;

            Console.CursorVisible = false;
            Console.Clear();

            var oldColor = Console.ForegroundColor;
            var oldBack = Console.BackgroundColor;

            int tPos = 1, sPos = 9;
            foreach (var tracker in Trackers)
            {
                Console.BackgroundColor = ConsoleColor.Blue;
                WriteAt(new Position(5, tPos), ConsoleColor.White, $"  {tracker.Value.Title}".PadRight(50));
                Console.BackgroundColor = oldBack;
                WriteAt(new Position(15, sPos), ConsoleColor.White, "Steps:");
                tPos += 10; sPos += 10;
            }

            ConsoleBox(new Position(60, 3), 17, 8, ConsoleColor.White);
            Console.BackgroundColor = ConsoleColor.Blue;
            WriteAt(new Position(61, 4), ConsoleColor.White, " COLOUR LEGEND ");
            Console.BackgroundColor = oldBack;
            WriteAt(new Position(64, 6), unsetColor, "idle");
            WriteAt(new Position(64, 7), evalColor, "Evaluating");
            WriteAt(new Position(64, 8), swapColor, "Changing");

            Console.ForegroundColor = oldColor;
        }

        static void WriteAt(Position pos, ConsoleColor color, string msg)
        {
            lock (lockObj)
            {
                Console.ForegroundColor = color;
                if (pos != null) Console.SetCursorPosition(pos.X, pos.Y);
                Console.Write(msg);
            }
        }

        static void PromptUser(string msg, bool isFlow = false)
        {
            WriteAt(isFlow ? null : new Position(5, promptPosition), Console.ForegroundColor, msg);
            Console.ReadKey();
        }
        static void ConsoleBox(Position pos, int width, int height, ConsoleColor foreColor)
        {
            var oldColor = Console.ForegroundColor;
            Console.OutputEncoding = Encoding.GetEncoding(866);
            Console.ForegroundColor = foreColor;

            var w = width - 2;
            var bar = string.Join("", Enumerable.Range(1, w).Select(_ => "─"));
            var top = new StringBuilder().Append("┌").Append(bar).Append("┐\n").ToString();
            var row = new StringBuilder().Append($"│{"".PadLeft(w)}│\n").ToString();
            var bot = new StringBuilder().Append("└").Append(bar).Append("┘").ToString();

            Console.SetCursorPosition(pos.X, pos.Y);
            Console.Write(top);
            for (int i = 1; i < height; i++)
            {
                Console.SetCursorPosition(pos.X, pos.Y + i);
                Console.Write(row);
            }
            Console.SetCursorPosition(pos.X, pos.Y + height - 1);
            Console.Write(bot);

            Console.ForegroundColor = oldColor;
        }

        #endregion
    }

    public enum FeedbackType
    {
        Evaluate,
        Swap,
        Done
    }

    public class Position
    {
        public Position(int x, int y)
        {
            X = x;
            Y = y;
        }
        public int X { get; set; }
        public int Y { get; set; }
    }

    public class SortTracker
    {
        public SortTracker() { }
        public SortTracker(string title,
                           ConsoleColor unsetColor, ConsoleColor evalColor, ConsoleColor swapColor,
                           List<Position> positions, Position stepPos, int sleepMS,
                           Action<Position, ConsoleColor, string> writer)
        {
            Title = title;
            UnsetColor = unsetColor;
            EvalColor = evalColor;
            SwapColor = swapColor;
            Positions = positions;
            SleepMS = sleepMS;
            stepPoint = stepPos;
            Writer = writer;
        }

        const int Unset = -1;

        public string Title { get; set; }
        public int StepCount { get; set; }
        public int[] Data { get; set; }

        private int[] pointers { get; } = new[] { Unset, Unset };
        private Position stepPoint;

        public int SleepMS { get; set; }
        public ConsoleColor UnsetColor { get; set; }
        public ConsoleColor EvalColor { get; set; }
        public ConsoleColor SwapColor { get; set; }
        public List<Position> Positions { get; set; }
        public Action<Position, ConsoleColor, string> Writer { get; set; }

        public void SetData(int[] data)
        {
            Data = data;
            StepCount = 0;
            pointers[0] = Unset;
            pointers[1] = Unset;
        }

        public Task ReportAsync(int ndx1, int ndx2, FeedbackType feedback, bool alternate)
        {
            switch (feedback)
            {
                case FeedbackType.Done:
                case FeedbackType.Evaluate:
                    if (pointers[0] != Unset)
                    {
                        Writer(Positions[pointers[0]], UnsetColor, Data[pointers[0]].ToString().PadLeft(5));
                        Writer(Positions[pointers[1]], UnsetColor, Data[pointers[1]].ToString().PadLeft(5));
                    }
                    if (ndx1 != -1)
                    {
                        pointers[0] = ndx1;
                        pointers[1] = ndx2;
                        Writer(Positions[ndx1], EvalColor, Data[ndx1].ToString().PadLeft(5));
                        Writer(Positions[ndx2], EvalColor, Data[ndx2].ToString().PadLeft(5));
                    }
                    break;

                case FeedbackType.Swap:
                    if (alternate) // some sort algorithms don't toggle eval/swap states
                    {
                        Writer(Positions[pointers[0]], UnsetColor, Data[pointers[0]].ToString().PadLeft(5));
                        Writer(Positions[pointers[1]], UnsetColor, Data[pointers[1]].ToString().PadLeft(5));
                        pointers[0] = ndx1;
                        pointers[1] = ndx2;
                    }
                    Writer(Positions[ndx1], SwapColor, Data[ndx1].ToString().PadLeft(5));
                    Writer(Positions[ndx2], SwapColor, Data[ndx2].ToString().PadLeft(5));
                    break;
            }
            Writer(stepPoint, UnsetColor, (++StepCount).ToString("N0"));
            return Task.Delay(SleepMS);
        }
    }

    public static class SortingHelperExtensions
    {
        public static Task BubbleSortDescendingAsync<T>(this IList<T> collection, IComparer<T> comparer, Func<int, int, FeedbackType, bool, Task> feedbackAsync = null)
            => Task.Run(async () =>
            {
                for (int i = 0; i < collection.Count - 1; i++)
                {
                    for (int index = 1; index < collection.Count - i; index++)
                    {
                        await feedbackAsync?.Invoke(index, index - 1, FeedbackType.Evaluate, false);
                        if (comparer.Compare(collection[index], collection[index - 1]) > 0)
                        {
                            collection.Swap(index - 1, index);
                            await feedbackAsync?.Invoke(index - 1, index, FeedbackType.Swap, false);
                        }
                    }
                }
                await feedbackAsync?.Invoke(-1, -1, FeedbackType.Done, false);
            });

        public static Task CombSortDescendingAsync<T>(this IList<T> collection, IComparer<T> comparer, Func<int, int, FeedbackType, bool, Task> feedbackAsync = null)
            => Task.Run(async () =>
            {
                double gap = collection.Count;
                bool swaps = true;

                while (gap > 1 || swaps)
                {
                    gap /= 1.247330950103979;
                    if (gap < 1) gap = 1;
                    int i = 0;
                    swaps = false;

                    while (i + gap < collection.Count)
                    {
                        int igap = i + (int)gap;
                        await feedbackAsync?.Invoke(i, igap, FeedbackType.Evaluate, false);
                        if (comparer.Compare(collection[i], collection[igap]) < 0)
                        {
                            collection.Swap(i, igap);
                            swaps = true;
                            await feedbackAsync?.Invoke(i, igap, FeedbackType.Swap, false);
                        }
                        i++;
                    }
                }
                await feedbackAsync?.Invoke(-1, -1, FeedbackType.Done, false);
            });

        public static Task CycleSortDescendingAsync<T>(this IList<T> collection, IComparer<T> comparer, Func<int, int, FeedbackType, bool, Task> feedbackAsync = null)
            => Task.Run(async () =>
            {
                for (int cycleStart = 0; cycleStart < collection.Count; cycleStart++)
                {
                    T item = collection[cycleStart];
                    int position = cycleStart;

                    do
                    {
                        int to = 0;
                        for (int i = 0; i < collection.Count; i++)
                        {
                            await feedbackAsync?.Invoke(position, i, FeedbackType.Evaluate, false);
                            if (i != cycleStart && comparer.Compare(collection[i], item) > 0)
                                to++;
                        }

                        if (position != to)
                        {
                            while (position != to && comparer.Compare(item, collection[to]) == 0)
                            {
                                await feedbackAsync?.Invoke(position, to, FeedbackType.Evaluate, false);
                                to++;
                            }
                            T temp = collection[to];
                            collection[to] = item;
                            item = temp;
                            await feedbackAsync?.Invoke(position, to, FeedbackType.Swap, true);
                            position = to;
                        }
                    } while (position != cycleStart);
                }
                await feedbackAsync?.Invoke(-1, -1, FeedbackType.Done, false);
            });

        public static Task GnomeSortDescendingAsync<T>(this IList<T> collection, IComparer<T> comparer, Func<int, int, FeedbackType, bool, Task> feedbackAsync = null)
            => Task.Run(async () =>
            {
                int pos = 1;
                while (pos < collection.Count)
                {
                    await feedbackAsync?.Invoke(pos, pos - 1, FeedbackType.Evaluate, false);
                    if (comparer.Compare(collection[pos], collection[pos - 1]) <= 0)
                        pos++;
                    else
                    {
                        collection.Swap(pos, pos - 1);
                        await feedbackAsync?.Invoke(pos, pos - 1, FeedbackType.Swap, false);
                        if (pos > 1) pos--;
                    }
                }
                await feedbackAsync?.Invoke(-1, -1, FeedbackType.Done, false);
            });

        public static Task InsertionSortAsync<T>(this IList<T> collection, IComparer<T> comparer, Func<int, int, FeedbackType, bool, Task> feedbackAsync = null)
            => Task.Run(async () =>
            {
                int j;
                for (int i = 1; i < collection.Count; i++)
                {
                    T value = collection[i];
                    j = i - 1;
                    await feedbackAsync?.Invoke(i, j, FeedbackType.Evaluate, false);
                    while ((j >= 0) && (comparer.Compare(collection[j], value) < 0))
                    {
                        collection[j + 1] = collection[j];
                        await feedbackAsync?.Invoke(j + 1, i, FeedbackType.Swap, true);
                        j--;
                    }
                    collection[j + 1] = value;
                    await feedbackAsync?.Invoke(j + 1, i, FeedbackType.Swap, true);
                }
                await feedbackAsync?.Invoke(-1, -1, FeedbackType.Done, false);
            });

        public static Task OddEvenSortDescendingAsync<T>(this IList<T> collection, IComparer<T> comparer, Func<int, int, FeedbackType, bool, Task> feedbackAsync = null)
            => Task.Run(async () =>
            {
                bool sorted = false;

                while (!sorted)
                {
                    sorted = true;
                    for (var i = 1; i < collection.Count - 1; i += 2)
                    {
                        await feedbackAsync?.Invoke(i, i + 1, FeedbackType.Evaluate, false);
                        if (comparer.Compare(collection[i], collection[i + 1]) < 0)
                        {
                            collection.Swap(i, i + 1);
                            sorted = false;
                            await feedbackAsync?.Invoke(i, i + 1, FeedbackType.Swap, false);
                        }
                    }

                    for (var i = 0; i < collection.Count - 1; i += 2)
                    {
                        await feedbackAsync?.Invoke(i, i + 1, FeedbackType.Evaluate, false);
                        if (comparer.Compare(collection[i], collection[i + 1]) < 0)
                        {
                            collection.Swap(i, i + 1);
                            sorted = false;
                            await feedbackAsync?.Invoke(i, i + 1, FeedbackType.Swap, false);
                        }
                    }
                }
                await feedbackAsync?.Invoke(-1, -1, FeedbackType.Done, false);
            });

        public static Task QuickSortAsync<T>(this IList<T> collection, IComparer<T> comparer, Func<int, int, FeedbackType, bool, Task> feedbackAsync = null)
            => Task.Run(async () =>
            {
                await collection.InternalQuickSortAsync(0, collection.Count - 1, comparer, feedbackAsync);
                await feedbackAsync?.Invoke(-1, -1, FeedbackType.Done, false);
            });

        private static async Task InternalQuickSortAsync<T>(this IList<T> collection, int leftmostIndex, int rightmostIndex, IComparer<T> comparer, Func<int, int, FeedbackType, bool, Task> feedbackAsync = null)
        {
            if (leftmostIndex < rightmostIndex)
            {
                int wallIndex = await collection.InternalPartitionAsync(leftmostIndex, rightmostIndex, comparer, feedbackAsync);
                await collection.InternalQuickSortAsync(leftmostIndex, wallIndex - 1, comparer, feedbackAsync);
                await collection.InternalQuickSortAsync(wallIndex + 1, rightmostIndex, comparer, feedbackAsync);
            }
        }

        private static async Task<int> InternalPartitionAsync<T>(this IList<T> collection, int leftmostIndex, int rightmostIndex, IComparer<T> comparer, Func<int, int, FeedbackType, bool, Task> feedbackAsync = null)
        {
            int wallIndex, pivotIndex;
            pivotIndex = rightmostIndex;
            T pivotValue = collection[pivotIndex];
            wallIndex = leftmostIndex;

            for (int i = leftmostIndex; i <= (rightmostIndex - 1); i++)
            {
                await feedbackAsync?.Invoke(i, pivotIndex, FeedbackType.Evaluate, false);
                if (comparer.Compare(collection[i], pivotValue) >= 0)
                {
                    collection.Swap(i, wallIndex);
                    await feedbackAsync?.Invoke(i, wallIndex, FeedbackType.Swap, true);
                    wallIndex++;
                }
            }
            collection.Swap(wallIndex, pivotIndex);
            await feedbackAsync?.Invoke(wallIndex, pivotIndex, FeedbackType.Swap, true);
            return wallIndex;
        }

        public static Task SelectionSortDescendingAsync<T>(this IList<T> collection, IComparer<T> comparer, Func<int, int, FeedbackType, bool, Task> feedbackAsync = null)
            => Task.Run(async () =>
            {
                for (int i = collection.Count - 1; i > 0; i--)
                {
                    int max = i;
                    for (int j = 0; j <= i; j++)
                    {
                        await feedbackAsync?.Invoke(j, max, FeedbackType.Evaluate, false);
                        if (comparer.Compare(collection[j], collection[max]) < 0)
                            max = j;
                    }
                    collection.Swap(i, max);
                    await feedbackAsync?.Invoke(i, max, FeedbackType.Swap, false);
                }
                await feedbackAsync?.Invoke(-1, -1, FeedbackType.Done, false);
            });

        public static Task ShellSortDescendingAsync<T>(this IList<T> collection, IComparer<T> comparer, Func<int, int, FeedbackType, bool, Task> feedbackAsync = null)
            => Task.Run(async () =>
            {
                bool flag = true;
                int d = collection.Count;

                while (flag || (d > 1))
                {
                    flag = false;
                    d = (d + 1) / 2;
                    for (int i = 0; i < (collection.Count - d); i++)
                    {
                        await feedbackAsync?.Invoke(i + d, i, FeedbackType.Evaluate, false);
                        if (comparer.Compare(collection[i + d], collection[i]) > 0)
                        {
                            collection.Swap(i + d, i);
                            flag = true;
                            await feedbackAsync?.Invoke(i + d, i, FeedbackType.Swap, false);
                        }
                    }
                }
                await feedbackAsync?.Invoke(-1, -1, FeedbackType.Done, false);
            });

        public static Task[] Execute(this Dictionary<SortType, SortTracker> trackers, int[] data)
        {
            var tasks = new Task[trackers.Count];
            for (int i = 0; i < trackers.Count; i++)
            {
                var tracker = trackers.ElementAt(i);
                tracker.Value.SetData(data.Select(x => x).ToArray());

                switch (tracker.Key)
                {
                    case SortType.Bubble:
                        tasks[i] = tracker.Value.Data.BubbleSortDescendingAsync(new CodeProjectComparer(), tracker.Value.ReportAsync);
                        break;
                    case SortType.Comb:
                        tasks[i] = tracker.Value.Data.CombSortDescendingAsync(new CodeProjectComparer(), tracker.Value.ReportAsync);
                        break;
                    case SortType.Cycle:
                        tasks[i] = tracker.Value.Data.CycleSortDescendingAsync(new CodeProjectComparer(), tracker.Value.ReportAsync);
                        break;
                    case SortType.Gnome:
                        tasks[i] = tracker.Value.Data.GnomeSortDescendingAsync(new CodeProjectComparer(), tracker.Value.ReportAsync);
                        break;
                    case SortType.Insertion:
                        tasks[i] = tracker.Value.Data.InsertionSortAsync(new CodeProjectComparer(), tracker.Value.ReportAsync);
                        break;
                    case SortType.OddEven:
                        tasks[i] = tracker.Value.Data.OddEvenSortDescendingAsync(new CodeProjectComparer(), tracker.Value.ReportAsync);
                        break;
                    case SortType.Quick:
                        tasks[i] = tracker.Value.Data.QuickSortAsync(new CodeProjectComparer(), tracker.Value.ReportAsync);
                        break;
                    case SortType.Selection:
                        tasks[i] = tracker.Value.Data.SelectionSortDescendingAsync(new CodeProjectComparer(), tracker.Value.ReportAsync);
                        break;
                    case SortType.Shell:
                        tasks[i] = tracker.Value.Data.ShellSortDescendingAsync(new CodeProjectComparer(), tracker.Value.ReportAsync);
                        break;
                }
            }
            return tasks;
        }
    }

    public static class CollectionExtensions
    {
        public static void Swap<T>(this IList<T> list, int firstIndex, int secondIndex)
        {
            if (list.Count < 2 || firstIndex == secondIndex) return;
            var temp = list[firstIndex];
            list[firstIndex] = list[secondIndex];
            list[secondIndex] = temp;
        }
    }
}

Static output does not convey how it perfoms but here is sample Output:
.
       Bubble Sort Descending

         9   95   94   93   89    8   84   63   55   52     ┌───────────────┐
        51   44   30  254  252  244   24  237  236  234     │ COLOUR LEGEND │
       229  227  222  216  214   21  205  201  196  189     │               │
       180  178  174  172   17  171  170  156  153  146     │   idle        │
        14  137  136  132  127  126  124  121  112  100     │   Evaluating  │
                                                            │   Changing    │
               Steps:  1,760                                │               │
                                                            └───────────────┘
       Comb Sort Descending

         9   95   94   93   89    8   84   63   55   52
        51   44   30  254  252  244   24  237  236  234
       229  227  222  216  214   21  205  201  196  189
       180  178  174  172   17  171  170  156  153  146
        14  137  136  132  127  126  124  121  112  100

               Steps:  797

       Cycle Sort Descending

         9   95   94   93   89    8   84   63   55   52
        51   44   30  254  252  244   24  237  236  234
       229  227  222  216  214   21  205  201  196  189
       180  178  174  172   17  171  170  156  153  146
        14  137  136  132  127  126  124  121  112  100

               Steps:  4,547

       Gnome Sort Descending

         9   95   94   93   89    8   84   63   55   52
        51   44   30  254  252  244   24  237  236  234
       229  227  222  216  214   21  205  201  196  189
       180  178  174  172   17  171  170  156  153  146
        14  137  136  132  127  126  124  121  112  100

               Steps:  1,649

       Insertion Sort

         9   95   94   93   89    8   84   63   55   52
        51   44   30  254  252  244   24  237  236  234
       229  227  222  216  214   21  205  201  196  189
       180  178  174  172   17  171  170  156  153  146
        14  137  136  132  127  126  124  121  112  100

               Steps:  633

       Odd-Even Sort Descending

         9   95   94   93   89    8   84   63   55   52
        51   44   30  254  252  244   24  237  236  234
       229  227  222  216  214   21  205  201  196  189
       180  178  174  172   17  171  170  156  153  146
        14  137  136  132  127  126  124  121  112  100

               Steps:  1,711

       Quick Sort

         9   95   94   93   89    8   84   63   55   52
        51   44   30  254  252  244   24  237  236  234
       229  227  222  216  214   21  205  201  196  189
       180  178  174  172   17  171  170  156  153  146
        14  137  136  132  127  126  124  121  112  100

               Steps:  424

       Selection Sort Descending

         9   95   94   93   89    8   84   63   55   52
        51   44   30  254  252  244   24  237  236  234
       229  227  222  216  214   21  205  201  196  189
       180  178  174  172   17  171  170  156  153  146
        14  137  136  132  127  126  124  121  112  100

               Steps:  1,324

       Shell Sort Descending

         9   95   94   93   89    8   84   63   55   52
        51   44   30  254  252  244   24  237  236  234
       229  227  222  216  214   21  205  201  196  189
       180  178  174  172   17  171  170  156  153  146
        14  137  136  132  127  126  124  121  112  100

               Steps:  722


     -- Press any key to exit --

To watch multiple sorts at the same time, I recommend selecting 3 or 4 differnt types of sorts plus resizing the console window before the they run. If you have the screen resolution (or set a very small font size), you could run all 9 at the same time like above!

Enjoy!
   
v5
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 6

Decided to use this as an exercise to get intimate with my new found love with Python. The solution is simple, I mean it. Just pad up a list of integers with zeros up to the max digits available in the list, sort them in descending order, then concatenate the original list of integers according to this order. It turned out I have fun flirting with the list and dictionary in Python, the result is shown:
"""
largest_possible_integer.py
by Peter Leow

Coding challenge: arrange numbers to form the largest possible integer

"""

import random

# Create a list of x number of random integers from 0 to 255
x = 50
list_int = random.sample(range(0,256), x)
print("List of {} positive integers (0 to 255): {}".format(x, list_int))

# Find the max integer in the list
max_int = max(list_int)
print("Max integer = {}".format(max_int))

# Find number of digits for this max integer
max_digit_count = len(str(max_int))
print("Most number of digits = {}".format(max_digit_count))

# Create a dictionary with list elements as keys and 
# their zero-padded values up to max_digit_count as values
dict_int = {}
for int in (list_int):
    dict_int[int] =  int * 10**(max_digit_count - len(str(int)))
print("Dictionary = {}".format(dict_int))

# Sort the original integer list based on the descending order
# of their padded values in the dictionary 
sorted_list_int = sorted(list_int, key=dict_int.__getitem__, reverse=True)

print("Sorted integer list to form the largest integer = {}".format(sorted_list_int))

largest_integer = ''.join(str(int) for int in sorted_list_int)

print("The largest integer is {}".format(largest_integer))
Try it out at this playground[^]. Change the x value and have fun! Disclaimer: Overflow at your own risk!
Two example outputs:
List of 10 positive integers (0 to 255): [93, 185, 191, 48, 209, 26, 36, 42, 25, 107]
Max integer = 209
Most number of digits = 3
Dictionary = {48: 480, 209: 209, 42: 420, 36: 360, 25: 250, 185: 185, 26: 260, 107: 107, 93: 930, 191: 191}
Sorted integer list to form the largest integer = [93, 48, 42, 36, 26, 25, 209, 191, 185, 107]
The largest integer is 934842362625209191185107
and
List of 50 positive integers (0 to 255): [26, 2, 246, 241, 107, 92, 215, 173, 119, 176, 156, 235, 125, 165, 129, 13, 19, 74, 227, 81, 248, 159, 102, 180, 90, 63, 123, 232, 217, 133, 224, 219, 98, 57, 95, 179, 245, 140, 151, 68, 254, 250, 117, 203, 9, 52, 222, 34, 41, 11]
Max integer = 254
Most number of digits = 3
Dictionary = {129: 129, 2: 200, 107: 107, 133: 133, 9: 900, 11: 110, 140: 140, 13: 130, 19: 190, 151: 151, 26: 260, 156: 156, 159: 159, 34: 340, 165: 165, 41: 410, 173: 173, 176: 176, 179: 179, 180: 180, 57: 570, 117: 117, 52: 520, 68: 680, 74: 740, 203: 203, 81: 810, 215: 215, 217: 217, 90: 900, 219: 219, 92: 920, 222: 222, 95: 950, 224: 224, 98: 980, 227: 227, 102: 102, 232: 232, 63: 630, 235: 235, 241: 241, 245: 245, 246: 246, 119: 119, 248: 248, 250: 250, 123: 123, 125: 125, 254: 254}
Sorted integer list to form the largest integer = [98, 95, 92, 90, 9, 81, 74, 68, 63, 57, 52, 41, 34, 26, 254, 250, 248, 246, 245, 241, 235, 232, 227, 224, 222, 219, 217, 215, 203, 2, 19, 180, 179, 176, 173, 165, 159, 156, 151, 140, 133, 13, 129, 125, 123, 119, 117, 11, 107, 102]
The largest integer is 9895929098174686357524134262542502482462452412352322272242222192172152032191801791761731651591561511401331312912512311911711107102
+++++[Round 2]+++++

ALerted by ppolymorphe. Found the mischief which is caused by single digits. See the corrected code:
"""
largest_possible_integer.py
by Peter Leow

Coding challenge: arrange numbers to form the largest possible integer

"""

import random

# Create a list of x number of random integers from 0 to 255
x = 50
# list_int = random.sample(range(0,256), x)

list_int = [26, 2, 246, 241, 107, 92, 215, 173, 119, 176, 156, 235, 125, 165, 129, 13, 19, 74, 227, 81, 248, 159, 102, 180, 90, 63, 123, 232, 217, 133, 224, 219, 98, 57, 95, 179, 245, 140, 151, 68, 254, 250, 117, 203, 9, 52, 222, 34, 41, 11]

print("List of {} positive integers (0 to 255): {}".format(x, list_int))

# Find the max integer in the list
max_int = max(list_int)
print("Max integer = {}".format(max_int))

# Find number of digits for this max integer
max_digit_count = len(str(max_int))
print("Most number of digits = {}".format(max_digit_count))

# Create a dictionary with list elements as keys and 
# their zero-padded values up to max_digit_count as values
dict_int = {}
for int in (list_int):
    length = len(str(int))
    
    if length == 1: # if single digit
        # make it the max in its range e.g. 2 to 299
        dict_int[int] =  (int + 1) * 10**(max_digit_count - length) - 1 
    else:
        dict_int[int] =  int * 10**(max_digit_count - length)
        
print("Dictionary = {}".format(dict_int))

# Sort the original integer list based on the descending order
# of their padded values in the dictionary 
sorted_list_int = sorted(list_int, key=dict_int.__getitem__, reverse=True)

print("Sorted integer list to form the largest integer = {}".format(sorted_list_int))

largest_integer = ''.join(str(int) for int in sorted_list_int)

print("The largest integer is {}".format(largest_integer))
Check it out: Python Playround[^] and the output:
List of 50 positive integers (0 to 255): [26, 2, 246, 241, 107, 92, 215, 173, 119, 176, 156, 235, 125, 165, 129, 13, 19, 74, 227, 81, 248, 159, 102, 180, 90, 63, 123, 232, 217, 133, 224, 219, 98, 57, 95, 179, 245, 140, 151, 68, 254, 250, 117, 203, 9, 52, 222, 34, 41, 11]
Max integer = 254
Most number of digits = 3
Dictionary = {129: 129, 2: 299, 107: 107, 133: 133, 9: 999, 11: 110, 140: 140, 13: 130, 19: 190, 151: 151, 26: 260, 156: 156, 159: 159, 34: 340, 165: 165, 41: 410, 173: 173, 176: 176, 179: 179, 180: 180, 57: 570, 117: 117, 52: 520, 68: 680, 74: 740, 203: 203, 81: 810, 215: 215, 217: 217, 90: 900, 219: 219, 92: 920, 222: 222, 95: 950, 224: 224, 98: 980, 227: 227, 102: 102, 232: 232, 63: 630, 235: 235, 241: 241, 245: 245, 246: 246, 119: 119, 248: 248, 250: 250, 123: 123, 125: 125, 254: 254}
Sorted integer list to form the largest integer = [9, 98, 95, 92, 90, 81, 74, 68, 63, 57, 52, 41, 34, 2, 26, 254, 250, 248, 246, 245, 241, 235, 232, 227, 224, 222, 219, 217, 215, 203, 19, 180, 179, 176, 173, 165, 159, 156, 151, 140, 133, 13, 129, 125, 123, 119, 117, 11, 107, 102]
The largest integer is 9989592908174686357524134226254250248246245241235232227224222219217215203191801791761731651591561511401331312912512311911711107102

+++++[round 3]+++++
I am back. It about my bed time after a busy day preparing for the coming Chinese New Year. Realizing the discussions that streaming in and the interests that generated, can't just walk away from it. Spare some time to ponder over it on how to fix this single digit mischief. I think I got it this time. See this:
"""
largest_possible_integer.py
by Peter Leow

Coding challenge: arrange numbers to form the largest possible integer

"""

import random

# Create a list of x number of random integers from 0 to 255
x = 50
# list_int = random.sample(range(0,256), x)


list_int = [26, 2, 246, 241, 107, 92, 215, 173, 119, 176, 156, 235, 125, 165, 129, 13, 19, 74, 227, 81, 248, 159, 102, 180, 90, 63, 123, 232, 217, 133, 224, 219, 98, 57, 95, 179, 245, 140, 151, 68, 254, 250, 117, 203, 9, 52, 222, 34, 41, 11]

print("List of {} positive integers (0 to 255): {}".format(x, list_int))

# Find the max integer in the list
max_int = max(list_int)
print("Max integer = {}".format(max_int))

# Find number of digits for this max integer
max_digit_count = len(str(max_int))
print("Most number of digits = {}".format(max_digit_count))

# Create a dictionary with list elements as keys and 
# their zero-padded values up to max_digit_count as values
dict_int = {}
for int in (list_int):
    length = len(str(int))
    
    if length == 1:
        
        temp = int
        
        for i in range(1, max_digit_count):
            temp += int * 10**i
        
        dict_int[int] = temp
    else:
        dict_int[int] =  int * 10**(max_digit_count - length)
        
print("Dictionary = {}".format(dict_int))

# Sort the original integer list based on the descending order
# of their padded values in the dictionary 
sorted_list_int = sorted(list_int, key=dict_int.__getitem__, reverse=True)

print("Sorted integer list to form the largest integer = {}".format(sorted_list_int))

largest_integer = ''.join(str(int) for int in sorted_list_int)

print("The largest integer is {}".format(largest_integer))
The output is:
List of 50 positive integers (0 to 255): [26, 2, 246, 241, 107, 92, 215, 173, 119, 176, 156, 235, 125, 165, 129, 13, 19, 74, 227, 81, 248, 159, 102, 180, 90, 63, 123, 232, 217, 133, 224, 219, 98, 57, 95, 179, 245, 140, 151, 68, 254, 250, 117, 203, 9, 52, 222, 34, 41, 11]
Max integer = 254
Most number of digits = 3
Dictionary = {129: 129, 2: 222, 107: 107, 133: 133, 9: 999, 11: 110, 140: 140, 13: 130, 19: 190, 151: 151, 26: 260, 156: 156, 159: 159, 34: 340, 165: 165, 41: 410, 173: 173, 176: 176, 179: 179, 180: 180, 57: 570, 117: 117, 52: 520, 68: 680, 74: 740, 203: 203, 81: 810, 215: 215, 217: 217, 90: 900, 219: 219, 92: 920, 222: 222, 95: 950, 224: 224, 98: 980, 227: 227, 102: 102, 232: 232, 63: 630, 235: 235, 241: 241, 245: 245, 246: 246, 119: 119, 248: 248, 250: 250, 123: 123, 125: 125, 254: 254}
Sorted integer list to form the largest integer = [9, 98, 95, 92, 90, 81, 74, 68, 63, 57, 52, 41, 34, 26, 254, 250, 248, 246, 245, 241, 235, 232, 227, 224, 2, 222, 219, 217, 215, 203, 19, 180, 179, 176, 173, 165, 159, 156, 151, 140, 133, 13, 129, 125, 123, 119, 117, 11, 107, 102]
The largest integer is 9989592908174686357524134262542502482462452412352322272242222219217215203191801791761731651591561511401331312912512311911711107102
Check it out online[^]Thank you for all the attention, I hope I did not disappoint you. Happy Chinese New Year!
   
v14
Comments
Patrice T 21-Jan-17 18:05pm
   
Last set
I think 2 is misplaced, it should be placed around 222.
Peter Leow 21-Jan-17 22:00pm
   
Why?
Patrice T 21-Jan-17 23:04pm
   
Because
224, 222, 219, 217, 215, 203, 2 -> 2242222192172152032
and
224, 222, 2, 219, 217, 215, 203 -> 2242222219217215203
Peter Leow 21-Jan-17 23:31pm
   
Got it, thanks.
Peter Leow 22-Jan-17 0:15am
   
Thank you for the feedback. Taken a second look. Found the culprit which are all the single digits. Put them to the max in their respective ranges, e.g. 2 => 299. Solved it.
Patrice T 22-Jan-17 0:29am
   
"e.g. 2 => 299. Solved it."
I fear padding is even more tricky.
Look at position of 2 in previous comment
Peter Leow 22-Jan-17 0:34am
   
You are right again. I rest the case. Got to get on with other business.
PIEBALDconsult 22-Jan-17 2:38am
   
Padding should be avoided.
Peter Leow 22-Jan-17 6:33am
   
Noted. Thanks.
Patrice T 22-Jan-17 8:34am
   
Depend on the method used.
How do you sort 242 and 24 without padding ?
PIEBALDconsult 22-Jan-17 10:45am
   
As per my second comparer. No padding or other string manipulation required. Just character compares and integer compares to break ties.
Patrice T 22-Jan-17 10:52am
   
Note that round 2 is still wrong.
2 go near 222
PIEBALDconsult 22-Jan-17 11:00am
   
Examples?
2 precedes 222
Patrice T 22-Jan-17 11:14am
   
In your last set
"2, 26, 254, 250, 248, 246, 245, 241, 235, 232, 227, 224, 222"
right answers are
"26, 254, 250, 248, 246, 245, 241, 235, 232, 227, 224, 222, 2"
"26, 254, 250, 248, 246, 245, 241, 235, 232, 227, 224, 2, 222"
PIEBALDconsult 22-Jan-17 11:25am
   
Mine produces:
26 254 250 248 246 245 241 235 232 227 224 2 222
Patrice T 22-Jan-17 11:35am
   
Ok output corrected.
Patrice T 22-Jan-17 12:04pm
   
If I understand your padding method, it fail on tricky values like {242, 24]
PIEBALDconsult 22-Jan-17 12:13pm
   
Mine produces:
242 24 ==> 24 242
Patrice T 22-Jan-17 12:24pm
   
Right answer.
In round 3, your code seems to pad 24 to 240 which should lead to 242 24, thus my doubt.
PIEBALDconsult 22-Jan-17 12:31pm
   
Mine does not pad at all, I think you replied to the wrong poster.
_If_ padding is done (and that will kill performance), then when comparing 24 with 242, the 24 should pad to 244 -- the last digit of the shorter value should be repeated enough times to make the strings equal in length.
Patrice T 22-Jan-17 12:41pm
   
Opps my bad, wrong poster.
"the last digit of the shorter value should be repeated enough times to make the strings equal in length."
In fact, wrong assumption, if you try 24, 242, 243, it should sort to 243 24 242
PIEBALDconsult 22-Jan-17 12:49pm
   
Aha... hmm...
Sooo... use the leading characters from the longer value to "pad" the shorter value maybe...
Patrice T 22-Jan-17 13:04pm
   
Since padding is interesting only when both values have same beginning, I pad a value by repeating itself. And I pad to maximum length+1.
24, 242, 243 get padded to 2424, 2422, 2432, then sort is obvious.
PIEBALDconsult 22-Jan-17 13:12pm
   
Just posted another comparer that shows another technique -- though I am still seeking a non-string-manipulating technique.
PIEBALDconsult 22-Jan-17 20:00pm
   
Well, you need only "pad" once you determine that the two values are identical up to the length of the shorter -- no need to "pad" prior to that.
Patrice T 22-Jan-17 20:12pm
   
Yes, it is useful only when you can't tell apart based on shorter length.
Always padding or not is matter of design decision.
Graeme_Grant 22-Jan-17 11:24am
   
I have explained it above in Solution 3.

{94, 9} = 949 - 994 = -45, change to {9, 94}. However, {1, 112} = 1112 - 1121 = -9, the correct order is {112, 1}.
PIEBALDconsult 22-Jan-17 11:27am
   
Yes, and mine does
94 9 1 112 ==> 9 94 112 1
Graeme_Grant 22-Jan-17 11:29am
   
I posted to you but was really for Peter (and others who might submit). :)
PIEBALDconsult 22-Jan-17 11:31am
   
Ah, OK.
Peter Leow 22-Jan-17 11:38am
   
Hi, guys. Sorry for keeping you waiting. I just noticed the discussion generated here and can't just go to bed. A quick think through and got a third version. Check it out and give me your view. But I have to go to bed lest late for work tomorrow.
Graeme_Grant 22-Jan-17 11:46am
   
Looks good Peter ... 2 & 222 are equals, so the order is not important. The outcome will be dependent on the algorithm used.

2:45am here, so understand how you feel. :)
PIEBALDconsult 22-Jan-17 11:49am
   
2 and 222 are equal, but 20 and 200 are not, which is why I give the shorter value precedence whenever my first compare says they're equal.
Graeme_Grant 22-Jan-17 11:51am
   
yes, "20"+"200" != "200"+"20" but "2"+"222" = "222"+"2". :)
PIEBALDconsult 22-Jan-17 12:58pm
   
Ah, now that would simplify things (for they who want to do use string manipulation); rather than "padding" the shorter value, simply compare the two candidate concatenations. :D That's much more straight-forward.

I just posted a new comparer that does that. It seems to work, but I want to avoid string manipulation. I have something whacky in mind...
Graeme_Grant 22-Jan-17 20:06pm
   
Okay okay, you don't want to use strings... After a little thought, I have updated Solution 3 (see bottom section) with a calculated version just for you. The same design principle still applies... ;)
PIEBALDconsult 22-Jan-17 20:10pm
   
No, I do want to use strings, you _have_ to use strings, but string _manipulation_ should be avoided.
Graeme_Grant 22-Jan-17 20:13pm
   
Look again, a "dedicated to PIEBALDConsult" no strings used new Comparer in Solution 3 (look down at the bottom of the solution, last Update).
Graeme_Grant 23-Jan-17 1:02am
   
I just had another look at your code and noticed that it is "all string" but above you mentioned "I want to avoid string manipulation"... I still like Linq over your SortedList implementation. Both use Microsoft's Quicksort algorithm under the covers. At least with Linq you only need to iterate over the list once - much cleaner and more efficient. ;)
PIEBALDconsult 23-Jan-17 9:03am
   
Right, no string _manipulation_ -- such as concatention and substring, I don't even want to use string compares. I have the one IndexOf per value to determines integerness, and I'd like to eliminate that.
You can _have_ strings, but not manipulate them.
Graeme_Grant 23-Jan-17 9:23am
   
Yes... That is why I dedicated an Integer only solution to you. The Concat in the new Comparer (solution 3) is not a string concatenation, it is an Integer Extension Method that is a binary merge of the two numbers joining or "Concatenates" the two numbers into one Int64 just like the string concatenation would. ;)
Graeme_Grant 22-Jan-17 20:10pm
   
I just posted a calculated version of my comparer. I think that this is a cleaner way of doing what you were trying to do.
Patrice T 22-Jan-17 12:54pm
   
Hi Peter,
As far as I understand you padding in round 3, it is wrong.
if you try 24, 242, 243, it should sort to 243 24 242, but I think you get 243 242 24.
Peter Leow 22-Jan-17 23:03pm
   
I will go with PIEBALDconsult.
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 8

Here is a quick WPF MVVM version that I put together over lunch using the Integer Comparer from my other solution (Solution 3).

I have used a custom SortableObservableCollection from an extended ObservableCollection passing a Comparer and will refresh the view via the binding system when the order of the elements change.

Here is the ViewModel & supporting classes:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

namespace WpfCodeChallenge
{
    public class CodeProjectComparer : IComparer<int>
    {
        int IComparer<int>.Compare(int x, int y) => (int)(Concat(x, y) - Concat(y, x));

        private Int64 Concat(Int32 x, Int32 y)
        {
            Int64 pow = 1;
            while (pow <= y) pow = ((pow << 2) + pow) << 1;
            return x * pow + y;
        }
    }

    public class SortableObservableCollection<T> : ObservableCollection<T>
    {
        public void SortDescending<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
        {
            ApplySort(Items.OrderByDescending(keySelector, comparer));
        }

        private void ApplySort(IEnumerable<T> sortedItems)
        {
            var sortedItemsList = sortedItems.ToList();
            foreach (var item in sortedItemsList)
                Move(IndexOf(item), sortedItemsList.IndexOf(item));
        }
    }

    public class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            Parse("94,9,99,98,111,11,112,1");
        }

        private IComparer<int> comparer = new CodeProjectComparer();
        public event PropertyChangedEventHandler PropertyChanged;

        public SortableObservableCollection<int> DataSet { get; set; }
            = new SortableObservableCollection<int>();

        private string csvValues;
        public string CsvValues { get { return csvValues; } set { Parse(value); } }

        private void Parse(string value)
        {
            csvValues = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CsvValues"));

            try
            {
                var values = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                          .Select(x => Convert.ToInt32(x));
                if (values.Any())
                {
                    DataSet.Clear();
                    foreach (var val in values)
                        DataSet.Add(val);
                    DataSet.SortDescending(x => x, comparer);
                }
            }
            catch { }
        }
    }
}

Here is the view (no code-behind used):
<Window x:Class="WpfCodeChallenge.MainWindow" Height="350" Width="525"

		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 

		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

		xmlns:vm="clr-namespace:WpfCodeChallenge"

		Title="Weekly Coding Challenge Code Project: Arrange Numbers">
	<Window.DataContext>
		<vm:MainViewModel/>
	</Window.DataContext>
	<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="50">
		<Grid.RowDefinitions>
			<RowDefinition Height="Auto"/>
			<RowDefinition MinHeight="100"/>
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="Auto"/>
			<ColumnDefinition MinWidth="200"/>
		</Grid.ColumnDefinitions>
		<TextBlock Text="Input csv numeric values:" Margin="4 10" 

                   VerticalAlignment="Top"/>
		<TextBox Text="{Binding CsvValues, UpdateSourceTrigger=PropertyChanged, Delay=200}" 

				 TextWrapping="Wrap" MaxLines="1" Grid.Column="1" Margin="0 10" 

                 VerticalAlignment="Top"/>
		<TextBlock Text="Output(sorted)" Grid.Row="1" Margin="4 10" MinHeight="100"/>
		<ListBox ItemsSource="{Binding DataSet}" Grid.Row="1" Grid.Column="1" Margin="0 10"

				 ScrollViewer.HorizontalScrollBarVisibility="Disabled"

				 ScrollViewer.VerticalScrollBarVisibility="Auto">
			<ListBox.ItemsPanel>
				<ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
			</ListBox.ItemsPanel>
		</ListBox>
	</Grid>
</Window>

Update: I have discovered an issue with the custom SortableObservableCollection - it does not correctly handle duplicates. So to keep code simple, I have changed to a CollectionViewSorce in Xaml. To apply the custom sorting using CodeProjectComparer, I have added manual wiring of the Comparer to the CollectionViewSorce.CustomSort property. This can not be wired up in Xaml. Also, as the CollectionViewSorce is in the Xaml, we need to wait until it is loaded. So we do this in the Listbox.Loaded event.

Here is the revised CodeProjectComparer and MainViewModel classes:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

namespace WpfCodeChallenge
{
    public class CodeProjectComparer : IComparer
    {
        public int Compare(object x, object y) 
            => (int)(Concat((int)y, (int)x) - Concat((int)x, (int)y));

        private Int64 Concat(Int32 x, Int32 y)
        {
            Int64 pow = 1;
            while (pow <= y) pow = ((pow << 2) + pow) << 1;
            return x * pow + y;
        }
    }

    public class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            Parse("94,9,99,98,111,11,112,1");
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public ObservableCollection<int> DataSet { get; set; }
            = new ObservableCollection<int>();

        private string csvValues;
        public string CsvValues { get { return csvValues; } set { Parse(value); } }

        private void Parse(string value)
        {
            csvValues = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CsvValues"));

            try
            {
                var values = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                  .Select(x => Convert.ToInt32(x));
                if (values.Any())
                {
                    DataSet.Clear();
                    foreach (var val in values) DataSet.Add(val);
                }
            }
            catch { }
        }
    }
}

Here is the view:
<Window x:Class="WpfCodeChallenge.MainWindow" Height="350" Width="525"

		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 

		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

		xmlns:vm="clr-namespace:WpfCodeChallenge"

		Title="Weekly Coding Challenge Code Project: Arrange Numbers">
	<Window.DataContext>
		<vm:MainViewModel/>
	</Window.DataContext>
	<Window.Resources>
		<CollectionViewSource x:Key="CsvData" Source="{Binding DataSet}" 

                                      IsLiveSortingRequested="True"/>
	</Window.Resources>
	<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="50">
		<Grid.RowDefinitions>
			<RowDefinition Height="Auto"/>
			<RowDefinition MinHeight="100"/>
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="Auto"/>
			<ColumnDefinition MinWidth="200"/>
		</Grid.ColumnDefinitions>
		<TextBlock Text="Input csv numeric values:" Margin="4 10" 

				   VerticalAlignment="Top"/>
		<TextBox Text="{Binding CsvValues, UpdateSourceTrigger=PropertyChanged, Delay=200}" 

				 TextWrapping="Wrap" MaxLines="1" Grid.Column="1" Margin="0 10" 

				 VerticalAlignment="Top"/>
		<TextBlock Text="Output(sorted)" Grid.Row="1" Margin="4 10" MinHeight="100"/>
		<ListBox ItemsSource="{Binding Source={StaticResource CsvData}}" 

				 Grid.Row="1" Grid.Column="1" Margin="0 10" Loaded="OnLoaded"

				 ScrollViewer.HorizontalScrollBarVisibility="Disabled"

				 ScrollViewer.VerticalScrollBarVisibility="Auto">
			<ListBox.ItemsPanel>
				<ItemsPanelTemplate>
					<WrapPanel Orientation="Horizontal"/>
				</ItemsPanelTemplate>
			</ListBox.ItemsPanel>
		</ListBox>
	</Grid>
</Window>

And lastly, here is the view code-behind:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfCodeChallenge
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            ((ListCollectionView)((ListBox)sender).ItemsSource).CustomSort
                = new CodeProjectComparer();
        }
    }
}

Update #2: Today I was working on a TreeView where custom sorting at branch level was required and realized that the same method could be used here. So here is another version using a ValueConverter removing the need for wiring up an event in the View's code-behind and a ViewModel.

We now bind the ListBox directly to the user input. The single line value converter, using Linq, will filter out invalid characters from the input string, convert it to a list and apply the sort via the CollectionViewSource.

Here is the revised ValueConverter:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Data;

namespace WpfCodeChallenge
{
    public class CodeProjectSortConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
             => new ListCollectionView(
                string.Join("", ((string)value).Where(x => x == ',' || (x > 47 && x < 58)))
                    .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => System.Convert.ToInt32(x)).ToList())
            { CustomSort = Comparer<int>.Create((x, y) => (int)(Concat(y, x) - Concat(x, y))) };

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            => value;

        private Int64 Concat(Int32 x, Int32 y)
        {
            Int64 pow = 1;
            while (pow <= y) pow = ((pow << 2) + pow) << 1;
            return x * pow + y;
        }
    }
}

And the View using the revised ValueConverter:
<Window x:Class="WpfCodeChallenge.MainWindow" Height="350" Width="525"

		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 

		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

		xmlns:c="clr-namespace:WpfCodeChallenge"

		Title="Weekly Coding Challenge Code Project: Arrange Numbers">
	<Window.Resources>
		<c:CodeProjectSortConverter x:Key="CodeProjectSortConverter"/>
	</Window.Resources>
	<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="50">
		<Grid.RowDefinitions>
			<RowDefinition Height="Auto"/>
			<RowDefinition MinHeight="100"/>
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="Auto"/>
			<ColumnDefinition MinWidth="200"/>
		</Grid.ColumnDefinitions>
		<TextBlock Text="Input csv numeric values:" Margin="4 10" 

				   VerticalAlignment="Top"/>
		<TextBox x:Name="DataSet" Text="94,9,99,98,111,11,112,1" 

				 TextWrapping="Wrap" MaxLines="1" Grid.Column="1" Margin="0 10" 

				 VerticalAlignment="Top"/>
		<TextBlock Text="Output(sorted)" Grid.Row="1" Margin="4 10" MinHeight="100"/>
		<ListBox ItemsSource="{Binding ElementName=DataSet, Path=Text, 
			Converter={StaticResource CodeProjectSortConverter}}" 

				 Grid.Row="1" Grid.Column="1" Margin="0 10"

				 ScrollViewer.HorizontalScrollBarVisibility="Disabled"

				 ScrollViewer.VerticalScrollBarVisibility="Auto">
			<ListBox.ItemsPanel>
				<ItemsPanelTemplate>
					<WrapPanel Orientation="Horizontal"/>
				</ItemsPanelTemplate>
			</ListBox.ItemsPanel>
		</ListBox>
	</Grid>
</Window>


WPF binding makes this cool ... you can edit the values in the IDE and the ListBox will update in real time! :)
   
v7

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month


Advertise | Privacy | Cookies | Terms of Service
Web03 | 2.8.190518.1 | Last Updated 26 Jan 2017
Copyright © CodeProject, 1999-2019
All Rights Reserved.
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100