ðŸŽ‰ Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io ðŸŽ‰

# SergiiVlasiuk's solution

## to Book Store in the Scala Track

Published at Aug 26 2019 · 0 comments
Instructions
Test suite
Solution

To try and encourage more sales of different books from a popular 5 book series, a bookshop has decided to offer discounts on multiple book purchases.

One copy of any of the five books costs \$8.

If, however, you buy two different books, you get a 5% discount on those two books.

If you buy 3 different books, you get a 10% discount.

If you buy 4 different books, you get a 20% discount.

If you buy all 5, you get a 25% discount.

Note: that if you buy four books, of which 3 are different titles, you get a 10% discount on the 3 that form part of a set, but the fourth book still costs \$8.

Your mission is to write a piece of code to calculate the price of any conceivable shopping basket (containing only books of the same series), giving as big a discount as possible.

For example, how much does this basket of books cost?

• 2 copies of the first book
• 2 copies of the second book
• 2 copies of the third book
• 1 copy of the fourth book
• 1 copy of the fifth book

One way of grouping these 8 books is:

• 1 group of 5 --> 25% discount (1st,2nd,3rd,4th,5th)
• +1 group of 3 --> 10% discount (1st,2nd,3rd)

This would give a total of:

• 5 books at a 25% discount
• +3 books at a 10% discount

Resulting in:

• 5 x (8 - 2.00) == 5 x 6.00 == \$30.00
• +3 x (8 - 0.80) == 3 x 7.20 == \$21.60

For a total of \$51.60

However, a different way to group these 8 books is:

• 1 group of 4 books --> 20% discount (1st,2nd,3rd,4th)
• +1 group of 4 books --> 20% discount (1st,2nd,3rd,5th)

This would give a total of:

• 4 books at a 20% discount
• +4 books at a 20% discount

Resulting in:

• 4 x (8 - 1.60) == 4 x 6.40 == \$25.60
• +4 x (8 - 1.60) == 4 x 6.40 == \$25.60

For a total of \$51.20

And \$51.20 is the price with the biggest discount.

The Scala exercises assume an SBT project scheme. The exercise solution source should be placed within the exercise directory/src/main/scala. The exercise unit tests can be found within the exercise directory/src/test/scala.

To run the tests simply run the command `sbt test` in the exercise directory.

For more detailed info about the Scala track see the help page.

## Source

Inspired by the harry potter kata from Cyber-Dojo. http://cyber-dojo.org

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### BookStoreTest.scala

``````import org.scalatest.{Matchers, FunSuite}

/** @version 1.4.0 */
class BookStoreTest extends FunSuite with Matchers {

test("Only a single book") {
BookStore.total(List(1)) should be (800)
}

test("Two of the same book") {
pending
BookStore.total(List(2, 2)) should be (1600)
}

pending
BookStore.total(List()) should be (0)
}

test("Two different books") {
pending
BookStore.total(List(1, 2)) should be (1520)
}

test("Three different books") {
pending
BookStore.total(List(1, 2, 3)) should be (2160)
}

test("Four different books") {
pending
BookStore.total(List(1, 2, 3, 4)) should be (2560)
}

test("Five different books") {
pending
BookStore.total(List(1, 2, 3, 4, 5)) should be (3000)
}

test("Two groups of four is cheaper than group of five plus group of three") {
pending
BookStore.total(List(1, 1, 2, 2, 3, 3, 4, 5)) should be (5120)
}

test("Two groups of four is cheaper than groups of five and three") {
pending
BookStore.total(List(1, 1, 2, 3, 4, 4, 5, 5)) should be (5120)
}

test("Group of four plus group of two is cheaper than two groups of three") {
pending
BookStore.total(List(1, 1, 2, 2, 3, 4)) should be (4080)
}

test("Two each of first 4 books and 1 copy each of rest") {
pending
BookStore.total(List(1, 1, 2, 2, 3, 3, 4, 4, 5)) should be (5560)
}

test("Two copies of each book") {
pending
BookStore.total(List(1, 1, 2, 2, 3, 3, 4, 4, 5, 5)) should be (6000)
}

test("Three copies of first book and 2 each of remaining") {
pending
BookStore.total(List(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1)) should be (6800)
}

test("Three each of first 2 books and 2 each of remaining books") {
pending
BookStore.total(List(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2)) should be (7520)
}

test("Four groups of four are cheaper than two groups each of five and three") {
pending
BookStore.total(List(1, 1, 2, 2, 3, 3, 4, 5, 1, 1, 2, 2, 3, 3, 4, 5)) should be (10240)
}
}``````
``````import scala.collection.mutable

object BookStore {
private val cache = mutable.Map[List[Int], Int]()
private val discount: Map[Int, Double] = Map(1 -> 1, 2 -> 0.95, 3 -> 0.9, 4 -> 0.8, 5 -> 0.75)
private val corePrice: Int = 800

def total(books: List[Int]): Int = cache.getOrElseUpdate(books, getTotal(books))

private def getTotal(books: List[Int]): Int = books match {
case List() => 0
case _ => books.toSet.subsets.filter(_.nonEmpty)
.map(group => price(group) + total(books diff group.toList)).min
}

private def price(group: Set[Int]): Int = (corePrice * group.size * discount(group.size)).toInt
}``````