Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting mixed numbers and strings

I have a list of strings that can contain a letter or a string representation of an int (max 2 digits). They need to be sorted either alphabetically or (when it is actually an int) on the numerical value it represents.

Example:

IList<string> input = new List<string>()
    {"a", 1.ToString(), 2.ToString(), "b", 10.ToString()};

input.OrderBy(s=>s)
  // 1
  // 10
  // 2
  // a
  // b

What I would want is

  // 1
  // 2
  // 10
  // a
  // b

I have some idea involving formatting it with trying to parse it, then if it is a successfull tryparse to format it with my own custom stringformatter to make it have preceding zeros. I'm hoping for something more simple and performant.

Edit
I ended up making an IComparer I dumped in my Utils library for later use.
While I was at it I threw doubles in the mix too.

public class MixedNumbersAndStringsComparer : IComparer<string> {
    public int Compare(string x, string y) {
        double xVal, yVal;

        if(double.TryParse(x, out xVal) && double.TryParse(y, out yVal))
            return xVal.CompareTo(yVal);
        else 
            return string.Compare(x, y);
    }
}

//Tested on int vs int, double vs double, int vs double, string vs int, string vs doubl, string vs string.
//Not gonna put those here
[TestMethod]
public void RealWorldTest()
{
    List<string> input = new List<string>() { "a", "1", "2,0", "b", "10" };
    List<string> expected = new List<string>() { "1", "2,0", "10", "a", "b" };
    input.Sort(new MixedNumbersAndStringsComparer());
    CollectionAssert.AreEquivalent(expected, input);
}
like image 361
Boris Callens Avatar asked Jun 23 '09 14:06

Boris Callens


People also ask

How do you sort text and mixed numbers?

Creating Sort Columns Select the column to the right of the mixed cell you want to sort (Employee Number in this example). Right-click the column labels and click Insert to add a column. Press CTRL-Y to repeat the action and add another column.

How do I sort numbers in a string?

Convert each element in the String array obtained in the previous step into an integer and store in into the integer array. The sort() method of the Arrays class accepts an array, sorts the contents of it in ascending order. Sort the integer array using this method.

How do you sort numbers with text prefix or suffix in Excel?

1. Select a blank cell beside the numbers with letter prefix or suffix, says Cell B2, type the formula =EXTRACTNUMBERS(A2,TRUE) (A2 is the cell of number with letter prefix or suffix) into it, and then drag this cell's AutoFill Handle to the range as you need.


2 Answers

Two ways come to mind, not sure which is more performant. Implement a custom IComparer:

class MyComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        int xVal, yVal;
        var xIsVal = int.TryParse( x, out xVal );
        var yIsVal = int.TryParse( y, out yVal );

        if (xIsVal && yIsVal)   // both are numbers...
            return xVal.CompareTo(yVal);
        if (!xIsVal && !yIsVal) // both are strings...
            return x.CompareTo(y);
        if (xIsVal)             // x is a number, sort first
            return -1;
        return 1;               // x is a string, sort last
    }
}

var input = new[] {"a", "1", "10", "b", "2", "c"};
var e = input.OrderBy( s => s, new MyComparer() );

Or, split the sequence into numbers and non-numbers, then sort each subgroup, finally join the sorted results; something like:

var input = new[] {"a", "1", "10", "b", "2", "c"};

var result = input.Where( s => s.All( x => char.IsDigit( x ) ) )
                  .OrderBy( r => { int z; int.TryParse( r, out z ); return z; } )
                  .Union( input.Where( m => m.Any( x => !char.IsDigit( x ) ) )
                               .OrderBy( q => q ) );
like image 162
LBushkin Avatar answered Nov 15 '22 12:11

LBushkin


Perhaps you could go with a more generic approach and use a natural sorting algorithm such as the C# implementation here.

like image 35
Nathan Baulch Avatar answered Nov 15 '22 13:11

Nathan Baulch