Reputation: 66430
Given a sequence of Price
objects, I want to map it to applyPromo
function if a condition, i.e. promo == "FOO"
applies, otherwise return the sequence as is.
This is my applyPromo:
val pricePromo = price => price.copy(amount = price.amount - someDiscount)
In a mutable way I probably would write it like this:
var prices: Seq[Price] = Seq(price1, price2, ...)
.map(doStuff)
.map(doSomeOtherStuff)
if (promo == "FOO") {
prices = prices.map(applyPromo)
}
prices
I was wondering if I could do something similar like this while keeping the immutable approach of scala. Instead of creating a temp var
, I prefer to keep the chain.
Pseudo-code:
val prices = Seq(price1, price2, ...)
prices
.map(dosStuff)
.map(doOtherStuff)
.mapIf(promo == "FOO", applyPromo)
I don't want to check the condition within the map function in this case, as it applies for all elements:
prices.map(price => {
if (promo == "FOO") {
applyDiscount(price)
} else
price
}
)
Upvotes: 0
Views: 1199
Reputation: 31192
oxbow_lakes has an interesting answer
Easy way solve to me is wrapping Seq
in a Option
context.
scala> case class Price(amount: Double)
defined class Price
when condition matches,
scala> val promo = "FOO"
promo: String = FOO
scala> Some(Seq(Price(1), Price(2), Price(3))).collect{
case prices if promo == "FOO" => prices.map { p => p.copy(p.amount - 1 )}
case prices => prices}
res6: Option[Seq[Price]] = Some(List(Price(0.0), Price(1.0), Price(2.0)))
when condition does not match
scala> val promo = "NOT-FOO"
promo: String = NOT-FOO
scala> Some(Seq(Price(1), Price(2), Price(3))).collect{
case prices if promo == "FOO" => prices.map { p => p.copy(p.amount - 1 )}
case prices => prices}
res7: Option[Seq[Price]] = Some(List(Price(1.0), Price(2.0), Price(3.0)))
Upvotes: 1
Reputation: 3922
I'd do it like this:
val maybePromo: (Price => Price) =
if(promo == "FOO") applyPromo else identity _
prices.map(maybePromo)
Or you can inline it within map
itself:
prices.map(if(promo == "FOO") applyPromo else identity)
Upvotes: 2
Reputation: 134260
In scalaz, a function A => A
is called an endomorphism and is a Monoid whose associative binary operation is function composition and whose identity is the identity function. This is useful because there is a bunch of syntax available where monoids are concerned. For example, scalaz adds the ??
operation to boolean along these lines:
def ??[A: Monoid](a: A) = if (self) a else Monoid[A].zero
Thus:
prices
.map(doStuff)
.map(doSomeOtherStuff)
.map(((promo === "FOO") ?? deductDiscount).run)
Where:
val deductDiscount: Endo[Price] = Endo(px => px.copy(amount = px.amount - someDiscount))
The above all requires
import scalaz._
import Scalaz._
Notes
===
is typesafe equals syntax??
is boolean syntaxUpvotes: 1
Reputation: 9100
You just need to use else
to make it functional (and you can create an implicit class
to add the mapIf
method if you prefer):
val prices: Seq[Price] = Seq(price1, price2,...).map(doStuff).map(doSomeOtherStuff)
/* val resultPrices = */ if (promo == "FOO") {
prices.map(price => {
price.copy(amount = price.amount - someDiscount)
})
} else prices
Something like this:
implicit class ConditionalMap[T](seq: Seq[T]) extends AnyVal {
def mapIf[Q](cond: =>Boolean, f: T => Q): Seq[Q] = if (cond) seq.map(f) else seq
}
You can also map(x => x)
in the else case:
val discountFunction = if (promo == "FOO") (price: Price) =>
price.copy(amount = price.amount - someDiscount) else (x: Price) => x
val prices: Seq[Price] = Seq(price1, price2,...).
map(doStuff).
map(doSomeOtherStuff).
map(discountFunction)
Upvotes: 2