Home > front end >  How do I sort numbers written in string format("one "two" three" etc.) according
How do I sort numbers written in string format("one "two" three" etc.) according

Time:03-02

public static IEnumerable<string> OrderingReversal()
{
    string[] digits = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };

    var res = digits.Where(w => w[1] == 'i')
                    .OrderByDescending(w => Convert.ToInt32(w)).ToList<string>();

    return res;
}

I would like the output to be: "nine", "eight", "six" and "five" I tried to achieve this by the code I provided above. However, I am receiving runtime error: System.FormatException: 'Input string was not in a correct format.'

Any Help would be appreciated. Thank you!

CodePudding user response:

A big part of the solution is found in other answers like this one but more is needed to sort the results.

Here's a class that will take a list of strings and return them sorted by the numeric values they represent:

public delegate long ConvertStringToNumberDelegate(string number);

public class NumbersAsTextSorter
{
    private readonly ConvertStringToNumberDelegate _convertStringToNumberFunction;

    public NumbersAsTextSorter(ConvertStringToNumberDelegate convertStringToNumberFunction)
    {
        _convertStringToNumberFunction = convertStringToNumberFunction;
    }

    public IEnumerable<string> ReverseSort(IEnumerable<string> input)
    {
        var textWithNumbers = input.Select(i => new {input = i, value = _convertStringToNumberFunction(i)});

        return textWithNumbers.OrderByDescending(t => t.value).Select(s => s.input);
    }
}

This will create a collection of text inputs matched with numeric values. It sorts the collection by the numeric value and then returns the text inputs in that sorted order.

As you can see, a critical detail is missing. How do you convert "one" to 1, "two" to 2, and so forth?

For that you need an implementation of the delegate - ConvertStringToNumberDelegate. You can get that by using the class in the the answer I referenced above.

For convenience I've copied that static method (for which I am explicitly not taking credit!) into a static class:

public static class TestToNumbers
{
    private static Dictionary<string, long> numberTable =
        new Dictionary<string, long>
        {{"zero",0},{"one",1},{"two",2},{"three",3},{"four",4},
        {"five",5},{"six",6},{"seven",7},{"eight",8},{"nine",9},
        {"ten",10},{"eleven",11},{"twelve",12},{"thirteen",13},
        {"fourteen",14},{"fifteen",15},{"sixteen",16},
        {"seventeen",17},{"eighteen",18},{"nineteen",19},{"twenty",20},
        {"thirty",30},{"forty",40},{"fifty",50},{"sixty",60},
        {"seventy",70},{"eighty",80},{"ninety",90},{"hundred",100},
        {"thousand",1000},{"million",1000000},{"billion",1000000000},
        {"trillion",1000000000000},{"quadrillion",1000000000000000},
        {"quintillion",1000000000000000000}};
    public static long ToLong(string numberString)
    {
        var numbers = Regex.Matches(numberString, @"\w ").Cast<Match>()
            .Select(m => m.Value.ToLowerInvariant())
            .Where(v => numberTable.ContainsKey(v))
            .Select(v => numberTable[v]);
        long acc = 0, total = 0L;
        foreach (var n in numbers)
        {
            if (n >= 1000)
            {
                total  = (acc * n);
                acc = 0;
            }
            else if (n >= 100)
            {
                acc *= n;
            }
            else acc  = n;
        }
        return (total   acc) * (numberString.StartsWith("minus",
            StringComparison.InvariantCultureIgnoreCase) ? -1 : 1);
    }
}

Now you can create an instance of NumbersAsTextSorter which uses that static method to convert text to numbers. I've done that in this unit test which passes:

[TestMethod]
public void TestSorting()
{
    var sorter = new NumbersAsTextSorter(TestToNumbers.ToLong);
    var output = sorter.ReverseSort(new string[] { "one", "three hundred", "zero" });
    Assert.IsTrue(output.SequenceEqual(new string[] { "three hundred", "one", "zero" }));
}

For clarity, this is passing in "one", "three hundred", and "zero" and asserting that after it's sorted, the sequence is "three hundred", "one", "zero".

CodePudding user response:

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] digits = { "Zero", "one", "two", "Three", "four", "five", "Six", "seven", "eight", "nine" };

            var asc = OrderNumberStringsByValue(digits);
            var dec = OrderNumberStringsByValue(digits, true);
            
            string[] myDigits = { "Eight", "nine", "Five", "six" };

            var myAsc = OrderNumberStringsByValue(myDigits);
            var myDec = OrderNumberStringsByValue(myDigits, true);

            Console.WriteLine($"asc:   {string.Join(", ", asc)}");
            Console.WriteLine($"dec:   {string.Join(", ", dec)}");
            Console.WriteLine($"myAsc: {string.Join(", ", myAsc)}");
            Console.WriteLine($"myDec: {string.Join(", ", myDec)}");

            Console.ReadLine();
        }

        public static IEnumerable<string> OrderNumberStringsByValue(IEnumerable<string> numbers, bool decending = false)
        {
            return numbers.OrderBy(n => (decending ? -1 : 1) * (int)Enum.Parse(typeof(Numbers), n.ToLower()));
        }
    }

    enum Numbers : int
    {
        zero = 0,
        one = 1,
        two = 2,
        three = 3,
        four = 4,
        five = 5,
        six = 6,
        seven = 7,
        eight = 8,
        nine = 9
    }
}
  •  Tags:  
  • c#
  • Related