Skip to content

Commit

Permalink
Merge pull request #38 from melange-re/discounts-lists
Browse files Browse the repository at this point in the history
Add 'Discounts Using Lists' chapter
  • Loading branch information
feihong authored Apr 24, 2024
2 parents 8c6c58f + f9f936f commit 00fcb68
Show file tree
Hide file tree
Showing 26 changed files with 1,391 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default defineConfig({
{ text: 'Sandwich Tests', link: '/sandwich-tests/' },
{ text: 'Cram Tests', link: '/cram-tests/' },
{ text: 'Burger Discounts', link: '/burger-discounts/' },
{ text: 'Discounts Using Lists', link: '/discounts-lists/' },
]
}
],
Expand Down
117 changes: 117 additions & 0 deletions docs/discounts-lists/Discount.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// #region get-free-burger
/** Buy 2 burgers, get 1 free */
let getFreeBurger = (items: list(Item.t)) => {
let prices =
items
|> List.filter(item =>
switch (item) {
| Item.Burger(_) => true
| Sandwich(_)
| Hotdog => false
}
)
|> List.map(Item.toPrice)
|> List.sort((x, y) => - compare(x, y));

switch (prices) {
| []
| [_] => None
| [_, cheaperPrice, ..._] => Some(cheaperPrice)
};
};
// #endregion get-free-burger

ignore(getFreeBurger);

// #region get-half-off
/** Buy 1+ burger with 1+ of every topping, get half off */
let getHalfOff = (items: list(Item.t)) => {
let meetsCondition =
items
|> List.exists(
fun
| Item.Burger({lettuce: true, tomatoes: true, onions, cheese, bacon})
when onions > 0 && cheese > 0 && bacon > 0 =>
true
| Burger(_)
| Sandwich(_)
| Hotdog => false,
);

switch (meetsCondition) {
| false => None
| true =>
let total =
items
|> List.fold_left((total, item) => total +. Item.toPrice(item), 0.0);
Some(total /. 2.0);
};
};
// #endregion get-half-off

// #region get-free-burger-improved
/** Buy 2 burgers, get 1 free */
let getFreeBurger = (items: list(Item.t)) => {
let prices =
items
|> List.filter_map(item =>
switch (item) {
| Item.Burger(burger) => Some(Item.Burger.toPrice(burger))
| Sandwich(_)
| Hotdog => None
}
)
|> List.sort((x, y) => - Float.compare(x, y));

switch (prices) {
| []
| [_] => None
| [_, cheaperPrice, ..._] => Some(cheaperPrice)
};
};
// #endregion get-free-burger-improved

ignore(getFreeBurger);

// #region get-free-burger-nth
let getFreeBurger = (items: list(Item.t)) => {
items
|> List.filter(item =>
switch (item) {
| Item.Burger(_) => true
| Sandwich(_)
| Hotdog => false
}
)
|> List.map(Item.toPrice)
|> List.sort((x, y) => - Float.compare(x, y))
|> ListSafe.nth(1);
};
// #endregion get-free-burger-nth

// #region get-free-burgers
/** Buy n burgers, get n/2 burgers free */
let getFreeBurgers = (items: list(Item.t)) => {
let prices =
items
|> List.filter_map(item =>
switch (item) {
| Item.Burger(burger) => Some(Item.Burger.toPrice(burger))
| Sandwich(_)
| Hotdog => None
}
);

switch (prices) {
| []
| [_] => None
| prices =>
let result =
prices
|> List.sort((x, y) => - Float.compare(x, y))
|> List.filteri((index, _) => index mod 2 == 1)
|> List.fold_left((+.), 0.0);
Some(result);
};
};
// #endregion get-free-burgers
11 changes: 11 additions & 0 deletions docs/discounts-lists/DiscountTests.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
open Fest;

// #region first-test
test("0 burgers, no discount", () =>
expect
|> equal(
Discount.getFreeBurger([Hotdog, Sandwich(Ham), Sandwich(Turducken)]),
None,
)
);
// #endregion first-test
52 changes: 52 additions & 0 deletions docs/discounts-lists/Item.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module Burger = {
type t = {
lettuce: bool,
onions: int,
cheese: int,
tomatoes: bool,
bacon: int,
};

let toPrice = ({onions, cheese, tomatoes, bacon, lettuce: _}) => {
let toppingCost = (quantity, cost) => float_of_int(quantity) *. cost;

15. // base cost
+. toppingCost(onions, 0.2)
+. toppingCost(cheese, 0.1)
+. (tomatoes ? 0.05 : 0.0)
+. toppingCost(bacon, 0.5);
};
};

module Sandwich = {
type t =
| Portabello
| Ham
| Unicorn
| Turducken;

let toPrice = (~date: Js.Date.t, t) => {
let day = date |> Js.Date.getDay |> int_of_float;

switch (t) {
| Portabello
| Ham => 10.
| Unicorn => 80.
| Turducken when day == 2 => 10.
| Turducken => 20.
};
};
};

type t =
| Sandwich(Sandwich.t)
| Burger(Burger.t)
| Hotdog;

let toPrice = t => {
switch (t) {
| Sandwich(sandwich) => Sandwich.toPrice(sandwich, ~date=Js.Date.make())
| Burger(burger) => Burger.toPrice(burger)
| Hotdog => 5.
};
};
2 changes: 2 additions & 0 deletions docs/discounts-lists/ListSafe.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/** Return the nth element encased in Some; if it doesn't exist, return None */
let nth = (n, list) => n < 0 ? None : List.nth_opt(list, n);
38 changes: 38 additions & 0 deletions docs/discounts-lists/Order.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
type t = list(Item.t);

module Format = {
let currency = _ => React.null;
};

module OrderItem = {
[@react.component]
let make = (~item as _: Item.t) => <div />;
};

let css = {"order": "", "total": ""};

// #region make
[@react.component]
let make = (~items: t) => {
let total =
items
|> ListLabels.fold_left(~init=0., ~f=(acc, order) =>
acc +. Item.toPrice(order)
);

<table className=css##order>
<tbody>
{items
|> List.mapi((index, item) =>
<OrderItem key={"item-" ++ string_of_int(index)} item />
)
|> Stdlib.Array.of_list
|> React.array}
<tr className=css##total>
<td> {React.string("Total")} </td>
<td> {total |> Format.currency} </td>
</tr>
</tbody>
</table>;
};
// #endregion make
6 changes: 6 additions & 0 deletions docs/discounts-lists/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(melange.emit
(target output)
(libraries reason-react melange-fest)
(preprocess
(pps melange.ppx reason-react-ppx))
(module_systems es6))
Binary file added docs/discounts-lists/function-popup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 00fcb68

Please sign in to comment.