-2
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!

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • 1
    The error is with the covert.ToInt32, "two" != "2". You'll need to assign a numeric value to each string. Check out https://www.tutorialspoint.com/keyvaluepair-in-chash as an example. – gman Mar 01 '22 at 17:58
  • 1
    `Select` has a helpful overload that includes the index which allows you to do this in one fell swoop: `digits.Select((word, value) => new { word, value }).Where(p => p.word.Contains("i")).OrderByDescending(p => p.value).Select(p => p.word)`. Do analyze the individual components to see how this works. – Jeroen Mostert Mar 01 '22 at 18:04
  • In your list the index is equal to the value of the word – Hans Kesting Mar 01 '22 at 18:05
  • Thank you Jeroen and everyone else who commented. For some reason i did not catch the fact that index == values. How do I mark it as solved? – Anonymous Future Developer Mar 01 '22 at 18:12
  • Your digits are sorted already. Why do you want to sort them? Just loop in reverse order and pick the ones with `"i"`. `for (int i = digits.Length - 1; i >= 0; i--) { if (digits[i][1] == "i") { yield return digits[i]; } }`. The [yield return](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield) produces the `IEnumerable`. – Olivier Jacot-Descombes Mar 01 '22 at 18:16
  • Thank You Mr. Olivier. The exercise requested the use of OrderBy LINQ function. I have been stuck on it for few hours and finally decided to post it here after I gave up to solve it myself. Mr. Jeroen's solution completely solved my problem. Thank you very much again! – Anonymous Future Developer Mar 01 '22 at 18:22

2 Answers2

1

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".

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
1
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
    }
}
rfmodulator
  • 3,638
  • 3
  • 18
  • 22