DEV Community

Discussion on: Daily Challenge #56 - Coffee Shop

Collapse
 
dak425 profile image
Donald Feury • Edited

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)
    }
}

Collapse
 
dak425 profile image
Donald Feury

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

Collapse
 
chachan profile image
Cherny

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"))
}
Collapse
 
dak425 profile image
Donald Feury • Edited

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.