0

I have to calculate the price of an order based on the input from the user using a Dictionary, but I can't seem to get it right.

  1. Chicken Strips - $3.50
  2. French Fries - $2.50
  3. Hamburger - $4.00
  4. Hotdog - $3.50
  5. Large Drink - $1.75
  6. Medium Drink - $1.50
  7. Milk Shake - $2.25
  8. Salad - $3.75
  9. Small Drink - $1.25

For example, if the input is 236, the price of the order should be $8.

Converting my LINQ query to double didn't work (my query is also definitely wrong). I don't really know any LINQ but as you can't really use a for loop for a dictionary as they aren't indexed, I don't really have any other choice.

        Dictionary<int, double> items = new Dictionary<int, double>()
        {
            { 1, 3.50 },
            { 2, 2.50 },
            { 3, 4.00 },
            { 4, 3.50 },
            { 5, 1.75 },
            { 6, 1.50 },
            { 7, 2.25 },
            { 8, 3.75 },
            { 9, 1.25 }
        };

        string order = Console.ReadLine();
        double sum = 0;
        for (int i = 0; i < order.Length; i++)
        {
            sum += Convert.ToDouble(items.Where(x => x.Key == int.Parse(order[i].ToString())).Select(x => x.Value));
        }
        Console.WriteLine(sum);
        Console.ReadKey();

Well, the output should be the correct amount of dollars based on the input from the user.

lapartman
  • 45
  • 1
  • 7
  • 2
    `sum += items[order[i]-'0'];` ? – Phil M Mar 25 '19 at 20:13
  • Seriously it was this simple? Can you elaborate on the -'0' part? I don't understand. – lapartman Mar 25 '19 at 20:18
  • 1
    `double sum = order.Sum(ch => items[ch-'0']);` - the `Select` returns each (ASCII) character from the input `order` string, they run from bytes with values 48-57 (where 48 is `0`) so subtract the base value `0` to get a number from 1 - 9. Note that this assumes an ASCII/similar character set where the digits are sequential and ascending. You could use `order.Where(ch => Char.IsDigit(ch))`... to ignore bad characters. – NetMage Mar 25 '19 at 20:23
  • Thank you for the explanation! – lapartman Mar 25 '19 at 20:27

4 Answers4

4

No need for a loop.

var sum = order.Sum(c => items[int.Parse(c.ToString())]);
djv
  • 15,168
  • 7
  • 48
  • 72
2

This implementation is quite fragile, it only allows 10 items and relies on string parsing but I think the following code does what you're asking:

order.Sum(character =>
{
    int id = character - '0';

    return items.TryGetValue(id, out double price) ? price : 0d;
});

TryGetValue checks that order only contains digits which are keys in the items dictionary.

vc 74
  • 37,131
  • 7
  • 73
  • 89
2

Try this

order.Select(c => (c - '0')).Where(c => c > 0 && c < 10).Sum(c => items[c])
Lana
  • 1,024
  • 1
  • 7
  • 14
  • 1
    Suggest using `Char.IsDigit` before converting to numeric. Also then just use `Sum(c => items[c-'0'])`, no need for `Select` twice. – NetMage Mar 25 '19 at 20:26
  • @NetMage `IsDigit` is [a bit broader](https://www.fileformat.info/info/unicode/category/Nd/list.htm) than [0-9] and would require [proper conversion](https://stackoverflow.com/questions/22063436/why-char-isdigit-returns-true-for-chars-which-cant-be-parsed-to-int) using `char.GetNumericValue()`. But indeed it would be cool as homework :) – Alexei Levenkov Mar 25 '19 at 20:59
  • @NetMage You are right about unnecessary last Select, I fix my solution. Thanks! – Lana Mar 26 '19 at 05:17
1

You can use sum += items[order[i]-'0']; in your for loop.

This works because '0' is stored with the value 48 (see the answers to this question). The rest of the digits are stored in order ('1' is 49, '2' is 50, etc), so '1' - '0' == 1 and so forth.

Phil M
  • 1,619
  • 1
  • 8
  • 10
  • I guess you need that second sentence as a comment in your code too though, to understand what it's doing. I'd stick with `int.Parse`any day. – Jesse de Wit Mar 25 '19 at 21:04
  • Maybe it's my background with C/C++, but I feel like every programmer should know that much about character encodings, at least. – Phil M Mar 25 '19 at 21:53