Skip to content

Commit

Permalink
Merge pull request #34 from melange-re/burger-discounts
Browse files Browse the repository at this point in the history
Add 'Burger Discounts' chapter
  • Loading branch information
feihong authored Apr 9, 2024
2 parents 8b66922 + 20a777b commit 36fc84e
Show file tree
Hide file tree
Showing 23 changed files with 1,616 additions and 10 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default defineConfig({
{ text: 'Better Burgers', link: '/better-burgers/' },
{ text: 'Sandwich Tests', link: '/sandwich-tests/' },
{ text: 'Cram Tests', link: '/cram-tests/' },
{ text: 'Burger Discounts', link: '/burger-discounts/' },
]
}
],
Expand Down
252 changes: 252 additions & 0 deletions docs/burger-discounts/Discount.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
let _ =
(items: array(Item.t)) => {
let burgers =
items
// #region type-annotate-argument
|> Js.Array.filter(~f=(item: Item.t) =>
switch (item) {
| Burger(_) => true
| Sandwich(_)
| Hotdog => false
}
);
// #endregion type-annotate-argument
ignore(burgers);
};

let _ =
(items: array(Item.t)) => {
let burgers =
items
// #region full-name-constructors
|> Js.Array.filter(~f=item =>
switch (item) {
| Item.Burger(_) => true
| Item.Sandwich(_)
| Item.Hotdog => false
}
);
// #endregion full-name-constructors
ignore(burgers);
};

let _ =
(items: array(Item.t)) => {
let burgers =
items
// #region full-name-constructor
|> Js.Array.filter(~f=item =>
switch (item) {
| Item.Burger(_) => true
| Sandwich(_)
| Hotdog => false
}
);
// #endregion full-name-constructor
ignore(burgers);
};

let _ =
(items: array(Item.t)) => {
let burgers =
items
// #region full-name-fun
|> Js.Array.filter(
~f=
fun
| Item.Burger(_) => true
| Sandwich(_)
| Hotdog => false,
);
// #endregion full-name-fun
ignore(burgers);
};

let _ =
(items: array(Item.t)) => {
// #region swap-function-order
items
|> Js.Array.filter(~f=item =>
switch (item) {
| Item.Burger(_) => true
| Sandwich(_)
| Hotdog => false
}
)
|> Js.Array.sortInPlaceWith(~f=(item1, item2) =>
- compare(Item.toPrice(item1), Item.toPrice(item2))
)
// #endregion swap-function-order
|> ignore;
};

let _ = {
let burgers: array(Item.t) = [||];

// #region match-on-tuple
switch (burgers[0], burgers[1]) {
| (Burger(_), Burger(cheaperBurger)) =>
Some(Item.Burger.toPrice(cheaperBurger))
| _ => None
};
// #endregion match-on-tuple
};

let _ = {
let burgers: array(Item.t) = [||];

// #region check-array-length
Js.Array.length(burgers) < 2
? None
: (
switch (burgers[0], burgers[1]) {
| (Burger(_), Burger(cheaperBurger)) =>
Some(Item.Burger.toPrice(cheaperBurger))
| _ => None
}
);
// #endregion check-array-length
};

let _ = {
let burgers: array(Item.t) = [||];

// #region catch-exception
switch (burgers[0], burgers[1]) {
| exception _ => None
| (Burger(_), Burger(cheaperBurger)) =>
Some(Item.Burger.toPrice(cheaperBurger))
| _ => None
};
// #endregion catch-exception
};

module Array = {
// #region module-array
// Safe array access function
let get: (array('a), int) => option('a) =
(array, index) =>
switch (index) {
| index when index < 0 || index >= Js.Array.length(array) => None
| index => Some(Stdlib.Array.get(array, index))
};
// #endregion module-array
};

let _ = {
let burgers: array(Item.t) = [||];

// #region custom-array-get
switch (burgers[0], burgers[1]) {
| (Some(Burger(_)), Some(Burger(cheaperBurger))) =>
Some(Item.Burger.toPrice(cheaperBurger))
| _ => None
};
// #endregion custom-array-get
};

// #region improved-get-free-burger
// Buy 2 burgers, get 1 free
let getFreeBurger = (items: array(Item.t)) => {
let burgers =
items
|> Js.Array.filter(~f=item =>
switch (item) {
| Item.Burger(_) => true
| Sandwich(_)
| Hotdog => false
}
)
|> Js.Array.map(~f=Item.toPrice)
|> Js.Array.sortInPlaceWith(~f=(x, y) => - compare(x, y));

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

// #region get-half-off-one
// Buy 1+ burger with 1 of every topping, get half off
let getHalfOff = (items: array(Item.t)) => {
let meetsCondition =
items
|> Js.Array.some(
~f=
fun
| Item.Burger({
lettuce: true,
tomatoes: true,
onions: 1,
cheese: 1,
bacon: 1,
}) =>
true
| Burger(_)
| Sandwich(_)
| Hotdog => false,
);

switch (meetsCondition) {
| false => None
| true =>
let total =
items
|> Js.Array.reduce(~init=0.0, ~f=(total, item) =>
total +. Item.toPrice(item)
);
Some(total /. 2.0);
};
};
// #endregion get-half-off-one
ignore(getHalfOff);

// #region get-half-off
// Buy 1+ burger with 1+ of every topping, get half off
let getHalfOff = (items: array(Item.t)) => {
let meetsCondition =
items
|> Js.Array.some(
~f=
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
|> Js.Array.reduce(~init=0.0, ~f=(total, item) =>
total +. Item.toPrice(item)
);
Some(total /. 2.0);
};
};
// #endregion get-half-off

let _ =
name => {
// #region return-variant-at-end
let result =
name
|> String.split_on_char(' ')
|> List.map(String.map(c => c |> Char.code |> (+)(1) |> Char.chr))
|> String.concat(" ")
|> String.cat("Hello, ");

Some(result);
// #endregion return-variant-at-end
};
122 changes: 122 additions & 0 deletions docs/burger-discounts/DiscountTests.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// #region first-three
open Fest;

test("0 burgers, no discount", () =>
expect
|> equal(
Discount.getFreeBurger([|
Hotdog,
Sandwich(Ham),
Sandwich(Turducken),
|]),
None,
)
);

test("1 burger, no discount", () =>
expect
|> equal(
Discount.getFreeBurger([|
Hotdog,
Sandwich(Ham),
Burger({
lettuce: false,
onions: 0,
cheese: 0,
tomatoes: false,
bacon: 0,
}),
|]),
None,
)
);

test("2 burgers of same price, discount", () =>
expect
|> equal(
Discount.getFreeBurger([|
Hotdog,
Burger({
lettuce: false,
onions: 0,
cheese: 0,
tomatoes: false,
bacon: 0,
}),
Sandwich(Ham),
Burger({
lettuce: false,
onions: 0,
cheese: 0,
tomatoes: false,
bacon: 0,
}),
|]),
Some(15.),
)
);
// #endregion first-three

// #region burger-record
let burger: Item.Burger.t = {
lettuce: false,
onions: 0,
cheese: 0,
tomatoes: false,
bacon: 0,
};
// #endregion burger-record

// #region refactor-use-burger-record
test("1 burger, no discount", () =>
expect
|> equal(
Discount.getFreeBurger([|Hotdog, Sandwich(Ham), Burger(burger)|]),
None,
)
);

test("2 burgers of same price, discount", () =>
expect
|> equal(
Discount.getFreeBurger([|
Hotdog,
Burger(burger),
Sandwich(Ham),
Burger(burger),
|]),
Some(15.),
)
);
// #endregion refactor-use-burger-record

// #region different-price-test
test("2 burgers of different price, discount of cheaper one", () =>
expect
|> equal(
Discount.getFreeBurger([|
Hotdog,
Burger({...burger, tomatoes: true}), // 15.05
Sandwich(Ham),
Burger({...burger, bacon: 2}) // 16.00
|]),
Some(15.05),
)
);
// #endregion different-price-test

// #region three-burgers
test("3 burgers of different price, return Some(15.15)", () =>
expect
|> equal(
Discount.getFreeBurger([|
Burger(burger), // 15
Hotdog,
Burger({...burger, tomatoes: true, cheese: 1}), // 15.15
Sandwich(Ham),
Burger({...burger, bacon: 2}) // 16.00
|]),
Some(15.15),
)
);
// #endregion three-burgers
Loading

0 comments on commit 36fc84e

Please sign in to comment.