We recently wanted to do some transformation on our small XML document in order to present some information on it. In this example, our example looks like this:
<basket> <item cost=".99" discountCode="A5">Last year's red Christmas baubles</item> <item cost=".10" discountCode="A5">Halloween lamps</item> <item cost="101.99" >Pine Tree</item> <item cost="20" discountCode="B2">Gold star</item> </basket>
It’s a pretty simple structure with some simple rules. A basket can contain one or more items. Each item has:
- A simple description
- A cost in dollars
- An optional discount code
The task we had was to display a list of human-readable discounts that had been applied to this particular basket. In the example given about the discount codes are A5 and B2.
We have a very simple list of known discounts, and can implement a simple conversion function:
object Discounts { def displayString(code : String): String = { code match { case "A5" => "Reduced to clear" case "B2" => "Pre-Christmas offer" } } }
We already had a class that wrapped this small XML document, creating a cached field for the item nodes.
class Basket(val rootNode: Node) { private lazy val items = (rootNode \ "item"); }
We can then apply a series of functions to convert the items:
class Basket(val rootNode: Node) { private lazy val items = (rootNode \ "item"); lazy val discountsApplied = items.map(item => item.attribute("discountCode") .map(attribute => displayString(attribute.text))) .flatten }
The above code pulls out all the “discountCode” attributes, and applies a conversion function to convert them to human-readable form. Since item.attribute returns an Option, we use flatten to get rid of elements that are essentially null.
Unfortunately, while there is a toSet method, and a list, there isn’t a great way to do both inbuilt. However, by constructing a TreeSet (a sorted set), we can append all the results and get the same effect. We end up with the following:
class Basket(val rootNode: Node) { private lazy val items = (rootNode \ "item"); lazy val discountsApplied = sortedUniqueSet( items.map(item => item.attribute("discountCode") .map(attribute => displayString(attribute.text))) .flatten) private def sortedUniqueSet(sequence: Seq[String]) = { TreeSet.empty[String] ++ sequence } }