loading...

Daily Challenge #56 - Coffee Shop

thepracticaldev profile image dev.to staff ・1 min read

Today, your challenge is to help Bob with his coffee shop!

Bob's coffee shop is really busy, so busy in fact that if you do not have the right amount of change, you won't get your coffee as Bob doesn't like waiting for people to sort change. Drinks avaialble are Americano £2.20, Latte £2.30, Flat white £2.40 and Filter £3.50

Write a function that returns, "Here is your "+type+", have a nice day!" if they have the right change or returns "Sorry, exact change only, try again tomorrow!"


Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

This challenge comes from victoriabate on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

pic
Editor guide
 

Well, here it is:

const pay = (amount) => {
  const beverage = {2.2:'Americano',2.3:'Latte',2.4:'Flat white',3.5:'Filter'}[amount];
  return beverage ? `Here is your ${beverage}, have a nice day!`:'Sorry, exact change only, try again tomorrow!';
}

Which outputs:

pay(2.2)
"Here is your Americano, have a nice day!"
pay(2.3)
"Here is your Latte, have a nice day!"
pay(2.30)
"Here is your Latte, have a nice day!"
pay(2.31)
"Sorry, exact change only, try again tomorrow!"
 

I also like it, and decided to rewrite it in c++

    const auto pay = [](float cash){
        const auto coffee = std::unordered_map<float, std::string>{
                {2.2, "Americano"},
                {2.3, "Latte"},
                {2.4, "Flat white"},
                {3.5, "Filter"},

        }[cash];

        return coffee.empty() ? "Sorry, exact change only, try again tomorrow!"
                              : "Here is your " + coffee + ", have a nice day!";
    };
 

Here is it smaller :)

(a,b={2.2:'Americano',2.3:'Latte',2.4:'Flat white',3.5:'Filter'}[a])=>b?`Here is your ${b}, have a nice day!`:`Sorry, exact change only, try again tomorrow!`
 
 

Good approach. What if the function is called like

pay('2.20')
 

What is the name for the

{a:'A', b:'B', c:'C'}[value]

notation? I seem to have managed to avoid seeing this for years!

 

The {} operator would define a map. In the solution that would be a map of float type keys for string type values. The [ ] is then used to access a particular key.

Awesome, thank you!

You're welcome!

 

I did it three different ways, but here's what I ended up with as probably the cleanest:

JS

const coffee = (amount) => {
  let drinktype = {
    2.2: "Americano",
    2.3: "Latte",
    2.4: "Flat white",
    3.5: "Filter",
  }[amount]

  if(drinktype) {
    return "Here is your "+drinktype+", have a nice day!"
  } else {
    return "Sorry, exact change only, try again tomorrow!"
  }
}

You can watch me solve it here, plus see the other two solutions (and a bonus discussion about why not to use floats for currency in JS 😂): youtu.be/HtvBlId7tig

 

I over engineered this but I felt like having a bit of fun with this one so lets have a Go shall we!?

coffee.go

package coffee

import "fmt"

// Product represents a type of product available at the coffee shop
type Product int

const (
    Americano Product = iota
    Latte
    FlatWhite
    Filter
)

const (
    // AmericanoPrice is the price of the product Americano
    AmericanoPrice float64 = 2.20
    // LattePrice is the price of the product Latte
    LattePrice float64 = 2.30
    // FlatWhitePrice is the price of the product FlatWhite
    FlatWhitePrice float64 = 2.40
    // FilterPrice is the price of the product Filter
    FilterPrice float64 = 3.50
)

const (
    exactChange string = "Here is your %s, have a nice day!"
    notExact    string = "Sorry, exact change only, try again tomorrow!"
)

// String gives the text representation of the product
func (p Product) String() string {
    switch p {
    case Americano:
        return "Americano"
    case Latte:
        return "Latte"
    case FlatWhite:
        return "Flat White"
    case Filter:
        return "Filter"
    default:
        return ""
    }
}

// Price returns of the price of the product
func (p Product) Price() float64 {
    switch p {
    case Americano:
        return AmericanoPrice
    case Latte:
        return LattePrice
    case FlatWhite:
        return FlatWhitePrice
    case Filter:
        return FilterPrice
    default:
        return float64(0)
    }
}

// Order represents an order submitted to the coffee shop
type Order struct {
    Items  []Product // Items is the collection of products being ordered
    Tender float64   // Tender is the amount of money given for the order
}

// Total calculates the total value of the order from all the products in it
func (o Order) Total() float64 {
    var total float64
    for _, item := range o.Items {
        total += item.Price()
    }
    return total
}

func (o Order) String() string {
    count := len(o.Items)

    switch count {
    case 0:
        return ""
    case 1:
        return o.Items[0].String()
    case 2:
        return fmt.Sprintf("%s and %s", o.Items[0].String(), o.Items[1].String())
    default:
        var str string

        for i, item := range o.Items {
            if i+1 == count {
                str += "and "
            }
            str += item.String()
            if i+1 != count {
                str += ", "
            }
        }

        return str
    }
}

// Purchase responds to an order submitted at the coffee shop
func Purchase(order Order) string {
    if order.Total() == order.Tender {
        return fmt.Sprintf(exactChange, order)
    }

    return notExact
}

coffee_test.go

package coffee

import "testing"

func TestPurchase(t *testing.T) {
    testCases := []struct {
        description string
        input       Order
        expected    string
    }{
        {
            "buy an Americano with exact change",
            Order{
                []Product{
                    Americano,
                },
                AmericanoPrice,
            },
            "Here is your Americano, have a nice day!",
        },
        {
            "buy an Americano without exact change",
            Order{
                []Product{
                    Americano,
                },
                2.30,
            },
            notExact,
        },
        {
            "buy a Latte with exact change",
            Order{
                []Product{
                    Latte,
                },
                LattePrice,
            },
            "Here is your Latte, have a nice day!",
        },
        {
            "buy a Latte without exact change",
            Order{
                []Product{
                    Latte,
                },
                5.00,
            },
            notExact,
        },
        {
            "buy a Flat White with exact change",
            Order{
                []Product{
                    FlatWhite,
                },
                FlatWhitePrice,
            },
            "Here is your Flat White, have a nice day!",
        },
        {
            "buy a Flat White without exact change",
            Order{
                []Product{
                    FlatWhite,
                },
                10.00,
            },
            notExact,
        },
        {
            "buy a Filter with exact change",
            Order{
                []Product{
                    Filter,
                },
                FilterPrice,
            },
            "Here is your Filter, have a nice day!",
        },
        {
            "buy a Filter without exact change",
            Order{
                []Product{
                    Filter,
                },
                4.00,
            },
            notExact,
        },
        {
            "buy two drinks with exact change",
            Order{
                []Product{
                    Americano,
                    FlatWhite,
                },
                AmericanoPrice + FlatWhitePrice,
            },
            "Here is your Americano and Flat White, have a nice day!",
        },
        {
            "buy two drinks without exact change",
            Order{
                []Product{
                    Filter,
                    Latte,
                },
                10.00,
            },
            notExact,
        },
        {
            "buy many drinks with exact change",
            Order{
                []Product{
                    Americano,
                    Americano,
                    FlatWhite,
                    Latte,
                    Filter,
                },
                (AmericanoPrice * 2) + FlatWhitePrice + LattePrice + FilterPrice,
            },
            "Here is your Americano, Americano, Flat White, Latte, and Filter, have a nice day!",
        },
        {
            "buy many drinks without exact change",
            Order{
                []Product{
                    Americano,
                    Americano,
                    FlatWhite,
                    Latte,
                    Filter,
                },
                30.00,
            },
            notExact,
        },
    }

    for _, test := range testCases {
        if result := Purchase(test.input); result != test.expected {
            t.Fatalf("FAIL: %s - Purchase(%+v): '%s' - expected '%s'", test.description, test.input, result, test.expected)
        }
        t.Logf("PASS: %s", test.description)
    }
}

 

I used this challenge as my "hello world" with Go. At first I tried a map thinking that it might be easy to have float64 type as key. Can you please post a solution using a map? Here's mine:

package main

import "fmt"

func buy(amount string) string {
    types := map[string]string{
        "2.2": "Americano",
        "2.3": "Latte",
        "2.4": "Flat white",
        "3.5": "Filter",
    }
    return fmt.Sprintf("Here is your %s have a nice day!", types[amount])
}

func main() {
    fmt.Println(buy("2.2"))
    fmt.Println(buy("3.5"))
}
 

Hello Cherny!

You can most certainly use floats as keys in a map, you just need to indicate what type the keys are in the map declaration.

For your code above you have:

map[string]string

If you want to use floats as the keys, you simply need to indicate as so:

map[float64]string

Then you could so something like this:

types := map[float64]string{
  2.2: "Americano",
  2.3: "Latte",
  2.4: "Flat White",
  3.5: "Filter",
}

That would also enable you to pass in a float as an argument to the func instead of a string as well.

Don't forget to include logic for checking if they have exact change!

If you would still like me to post a solution using a map I would be more than happy to.

Note

In Golang, if you attempt to access a value of a map with a key that doesn't exist, you will get the zero value of the type stored in the map. That may be useful for the exact change logic check in your solution.

If we use your map for example:

types[5.0]

would return "", since the key 5.0 doesn't exist and "" is the zero value for a string in Golang.

 

Tweaked to allow purchasing multiple drinks, inspired by Ben Halpern's solution.

 

I feel it isn't super explicit that people can only order one drink here, so maybe the function should account for that, eh?

In Ruby

DRINK_COSTS = { "Americano" => 2.2, "Latte" => 2.3, "Flat white" => 2.4, "Filter" => 3.50 }.freeze

def buy(drinks, cash)
    costs = drinks.map { |drink| DRINK_COSTS[drink] }
    response = "Sorry, exact change only, try again tomorrow!"
    response = if costs == cash && drinks.size > 1
                   "Here are your beverages, have a nice day!"
               elsif costs == cash
                   "Here is your #{drinks.first}, have a nice day!"
               end
end

This would expect drinks to be an array of strings. We could also do something were we could accept either a string or an array with something like drinks = [drinks].flatten where we put the result in an array and then flatten it each time. That way we don't have to worry about the type we get (since we aren't checking for it due to this being Ruby).

 

I had actually thought about that myself but chose not to consider it at the moment. I might go back and tweak mine to take a collection of drinks as well.

 

F#, pattern matching + Option to make it at least a little interesting:

let coffee price =
    price
    |> function
    | 2.2 -> Some "Americano"
    | 2.3 -> Some "Latte"
    | 2.4 -> Some "Flat white"
    | 3.5 -> Some "Filter"
    | _ -> None
    |> Option.map (sprintf "Here is your %s, have a nice day!") 
    |> Option.defaultValue "Sorry, exact change only, try again tomorrow!"
 
 

Haskell

import qualified Data.Map.Strict as M

coffees = M.fromList [ (2.2, "Americano")
                     , (2.3, "Latte") 
                     , (2.4, "Flat white") 
                     , (3.5, "Filter")
                     ]

coffee :: Double -> String
coffee price = let maybeName = M.lookup price coffees
               in case maybeName of
                 Nothing -> "Sorry, exact change only. Try again tomorrow!" 
                 Just name -> "Here is your " ++ name ++ ", have a nice day!" 
 

Am I missing something or is the import of isJust a leftover from a previous version, i.e. did you have an if instead of the match expression before?

 

You are correct. I edited my original comment.

 

My take at the challenge written in Haskell this time!

changeToMessage :: Float -> String
changeToMessage change 
    | change == 2.2 = "Here is your Americano, have a nice day!"
    | change == 2.3 = "Here is your Late, have a nice day!"
    | change == 2.4 = "Here is your Flat White, have a nice day!"
    | change == 3.5 = "Here is your Filter, have a nice day!"
    | otherwise = "Sorry, exact change only, try again tomorrow!

Try it online.

 

Python solution

from decimal import Decimal

def pay(amount: Decimal) -> str:
  drink = {
    2.2: 'Americano',
    2.3: 'Latte',
    2.4: 'Flat white',
    3.5: 'Filter'
  }.get(amount)

  error_message = 'Sorry, exact change only, try again tomorrow!'
  success_message = f'Here is your {drink}, have a nice day'

  return error_message if not drink else success_message

Remember, always use decimal for currency

 

My solution in js

const getACoffee = (amount) => {
  const menu = {'2.2': 'Americano', '2.3': 'Latte', '2.4': 'Flat white', '3.5': 'Filter'},
    coffeeType = menu[`${parseFloat(amount)}`];
  return coffeeType ? `Here is your ${coffeeType}, have a nice day!` : 'Sorry, exact change only, try again tomorrow!'
};
 

Erlang solution with tests.

-module(coffee).
-include_lib("eunit/include/eunit.hrl").
-export([coffee/1]).

coffee(X) ->
    P = coffee_price(X),
    case P of
        false -> "Sorry, exact change only, try again tomorrow!";
        _     -> "Here is your " ++ P ++ ", have a nice day!"
    end.

coffee_price(2.20) -> "Americano";
coffee_price(2.30) -> "Latte";
coffee_price(2.40) -> "Flat white";
coffee_price(3.50) -> "Filter";
coffee_price(_)    -> false.

americano_test() ->
    ?assert(coffee(2.20) == "Here is your Americano, have a nice day!").
latte_test() ->
    ?assert(coffee(2.30) == "Here is your Latte, have a nice day!").
flat_white_test() ->
    ?assert(coffee(2.40) == "Here is your Flat white, have a nice day!").
filter_test() ->
    ?assert(coffee(3.50) == "Here is your Filter, have a nice day!").
invalid_test() ->
    ?assert(coffee(4.90) == "Sorry, exact change only, try again tomorrow!").
 
const coffeeAmounts = {
    'Americano': 2.20,
    'Latte': 2.30,
    'Flat White': 2.40,
    'Filter': 3.50
}

const coffee = (coffeeType, cash) => {
    return coffeeAmounts[coffeeType] === cash 
      ? "Here is your " + coffeeType + ", have a nice day!"
      : "Sorry, exact change only, try again tomorrow!";
}
 

A simple Javascript solution using nested ternary operators.

const coffeeShop = (order = "Water", cash = 0) => {
  const drinks = [
    { name: "Americano", cost: 2.2 },
    { name: "Latte", cost: 2.3 },
    { name: "Flat White", cost: 2.4 },
    { name: "Filter", cost: 3.5 }
  ];

  const request = drinks.filter(drink => drink.name == order);
  request[0] == null
    ? console.log(`We do not serve ${order} here. Try some Filter tomorrow.`)
    : request[0].cost == cash
    ? console.log(`Here is your ${request[0].name}, have a nice day!`)
    : console.log("Sorry, exact change only, try again tomorrow!");
};

Works fine

coffeeShop(); //We do not serve Water here. Try some Filter tomorrow!
coffeeShop("Americano", 2.2); //Here is your Americano, have a nice day!
coffeeShop("Americano", 2.5); //Sorry, exact change only, try again tomorrow!
coffeeShop("Filter", 3.5); //Here is your Filter, have a nice day!
coffeeShop("Mocha", 3.5); //We do not serve Mocha here. Try some Filter tomorrow!!
 

Scala:

object CoffeeShop {
    private lazy val coffees = Map(
        2.2 -> "Americano",
        2.3 -> "Flat white",
        2.4 -> "Latte",
        3.5 -> "Filter"
    )

    //desired method
    def makeCoffee(change: Double): String = {
        coffees.get(change).fold("Sorry, exact change only, try again tomorrow!") {coffee => 
            s"Here is your $coffee, have a nice day!"
        }
    }
}
 

Here's mine:

function getCoffee(change) {
  const changeSum = change.reduce((a,b) => a + b);

  switch(changeSum) {
    case(2.20):
      return ("Here is your Americano, have a nice day!");
      break;
    case(2.30):
      return ("Here is your Latte, have a nice day!");
      break;
    case(2.40):
      return ("Here is your Flat white, have a nice day!");
      break;
    case(3.50):
      return ("Here is your Filter, have a nice day!");
       break;
    default:
      return ("Sorry, exact change only, try again tomorrow!");
  }
}
 

x86_64 assembly (System V ABI, GNU assembler), using the C standard library for printf.

coffee.S:

    .global coffee

    .text
coffee:
    push %rbp
    mov %rsp, %rbp

    mov $prices, %rsi
    xor %eax, %eax #offset

    mov $4, %ecx
loop:
    movlpd (%rsi, %rax, 8), %xmm1

    cmpeqsd %xmm0, %xmm1

    sub $8, %rsp
    movlpd %xmm1, (%rsp)
    pop %rdx

    cmp $0, %rdx
    jne goodjob

    inc %rax
    loop loop

    mov $badfmt, %rdi
    xor %eax, %eax
end:
    call printf

    pop %rbp
    ret

goodjob:
    mov $goodfmt, %rdi
    mov $names, %rsi
    mov (%rsi, %rax, 8), %rsi
    mov $1, %eax

    jmp end

    .section .rodata
prices:
    .double 2.20
    .double 2.30
    .double 2.40
    .double 3.50

names:
    .quad first
    .quad second
    .quad third
    .quad fourth

first:
    .asciz "Americano"
second:
    .asciz "Latte"
third:
    .asciz "Flat white"
fourth:
    .asciz "Filter"

goodfmt:
    .asciz "Here is your %s, have a nice day!\n"
badfmt:
    .asciz "Sorry, exact change only, try again tomorrow!\n"

coffee.h:

void coffee(double d);
 

Here you have a version using only ruby hashes:

def bob(type: 'water', money: '0.0')
  exact = {
     'americano' => '2.20',
     'latte'=> '2.30',
     'flat white'=>'2.40',
     'filter'=> '3.50'
  }[type.to_s.downcase] == money

  {
    true => "Here is your #{type.to_s}, have a nice day!",
    false =>  'Sorry, exact change only, try again tomorrow!'
  }[exact]
end

Outputs:

puts bob()
# Sorry, exact change only, try again tomorrow!

puts bob(type: 'Latte', money: '2.50')
# Sorry, exact change only, try again tomorrow!

puts bob(type: 'Latte', money: '2.30')
# Here is your Latte, have a nice day!
 

Python

def getCoffee(smallChange,type):
    menu = {'Americano' : 2.20, 'Latte' : 2.30, 'Flat white' : 2.40 , 'Filter' : 3.50}
    if sum(smallChange) == menu[type]:  return "Here is your %s, have a nice day!" %(type)
    else:   return "Sorry, exact change only, try again tomorrow!"