-
-
Save raboof/a589af9ecce79bb14f9e to your computer and use it in GitHub Desktop.
Functional 'add to basket' experiments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def addItem(newItem: BasketItem): BasketState = { | |
copy( | |
items.foldRight((false, List.empty[BasketItem])) { | |
case (item, (_, out)) if (item.matchesProductAndSizeOf(newItem)) ⇒ (true, item.incrementNumberBy(newItem.numberOfProducts) :: out) | |
case (item, (didSomethingMatch, out)) ⇒ (didSomethingMatch, item :: out) | |
} match { | |
case (false, _) ⇒ newItem :: items | |
case (true, modifiedItems) ⇒ modifiedItems | |
} | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Taking advantage of the convention that Left holds an unsuccessful (partial) result and Right a successful (partial) result: | |
def addItem(newItem: BasketItem, basket: List[BasketItem]) = { | |
basket.foldRight[Either[List[BasketItem], List[BasketItem]]](Left(List())) { | |
case (item, Right(items)) ⇒ Right(item :: items) | |
case (item @ BasketItem(newItem.productNumber, newItem.sizeCode, _, _), Left(items)) ⇒ Right(item.copy(numberOfProducts = item.numberOfProducts + newItem.numberOfProducts) :: items) | |
case (item, Left(items)) ⇒ Left(item :: items) | |
}.right.getOrElse(newItem :: basket) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class BasketState(items: List[BasketItem] = Nil) { | |
def addItem(newItem: BasketItem): BasketState = { | |
// if (items.exists(item ⇒ item.productNumber == newItem.productNumber && item.sizeCode == newItem.sizeCode)) { | |
// copy( | |
// items.map { item ⇒ | |
// if (item.productNumber == newItem.productNumber && item.sizeCode == newItem.sizeCode) | |
// item.copy(numberOfProducts = item.numberOfProducts + 1) | |
// else item | |
// } | |
// ) | |
// } else { | |
// copy(newItem :: items) | |
// } | |
// copy( | |
// items.foldRight((false, List.empty[BasketItem])) { (item, out) ⇒ | |
// if (item.productNumber == newItem.productNumber && item.sizeCode == newItem.sizeCode) | |
// (true, item.copy(numberOfProducts = item.numberOfProducts + 1) :: out._2) | |
// else (false, item :: out._2) | |
// } match { | |
// case (false, _) ⇒ newItem :: items | |
// case (true, modifiedItems) ⇒ modifiedItems | |
// } | |
// ) | |
copy( | |
items.foldRight((false, List.empty[BasketItem])) { | |
case (item, (_, out)) if (item.productNumber == newItem.productNumber && item.sizeCode == newItem.sizeCode) ⇒ (true, item.copy(numberOfProducts = item.numberOfProducts + newItem.numberOfProducts) :: out) | |
case (item, (bool, out)) ⇒ (bool, item :: out) | |
} match { | |
case (false, _) ⇒ newItem :: items | |
case (true, modifiedItems) ⇒ modifiedItems | |
} | |
) | |
// copy( | |
// items.foldRight((false, List.empty[BasketItem])) { | |
// case (BasketItem(newItem.productNumber, newItem.sizeCode, numberOfProducts, product), (_, out)) ⇒ (true, BasketItem(newItem.productNumber, newItem.sizeCode, numberOfProducts + newItem.numberOfProducts, product) :: out) | |
// case (item, (bool, out)) ⇒ (bool, item :: out) | |
// } match { | |
// case (false, _) ⇒ newItem :: items | |
// case (true, modifiedItems) ⇒ modifiedItems | |
// } | |
// ) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The 'classical' functional solution is of course recursive: | |
def addRecursiveRight(newItem: BasketItem, basket: List[BasketItem]): List[BasketItem] = basket match { | |
case item :: tail ⇒ item match { | |
case BasketItem(newItem.productNumber, newItem.sizeCode, _, _) ⇒ item.copy(numberOfProducts = item.numberOfProducts + newItem.numberOfProducts) :: tail | |
case _ ⇒ item :: addRecursiveRight(newItem, tail) | |
} | |
case List() ⇒ List(newItem) | |
} | |
//.. but that adds a new item to the right, not to the left, and is not tail-recursive. | |
// I fooled around a bit to get to a tail-recursive solution that adds to the left, but didn't come up with anything elegant - best I could do was: | |
def addRecursiveLeft(newItem: BasketItem, basket: List[BasketItem]): List[BasketItem] = addRecursiveImpl(newItem, basket).getOrElse(newItem :: basket) | |
@tailrec | |
final def addRecursiveImpl(newItem: BasketItem, basket: List[BasketItem], skippedItems: List[BasketItem] = List()): Option[List[BasketItem]] = { | |
basket match { | |
case item :: tail ⇒ item match { | |
case BasketItem(newItem.productNumber, newItem.sizeCode, _, _) ⇒ Some((item.copy(numberOfProducts = item.numberOfProducts + newItem.numberOfProducts) :: skippedItems).reverse ::: tail) | |
case _ ⇒ addRecursiveImpl(newItem, tail, item :: skippedItems) | |
} | |
case List() ⇒ None | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice! Finally a good use case for
Either
;)My version of the
Either
solution, slightly cleaned up for readability: