建立有条件地列出避免突变

时间:2018-05-04 09:00:00

标签: scala collections

假设我想有条件地建立比萨的成分列表:

val ingredients = scala.collection.mutable.ArrayBuffer("tomatoes", "cheese")

if (!isVegetarian()) {
   ingredients += "Pepperoni"  
}

if (shouldBeSpicy()) {
   ingredients += "Jalapeno"
}

//etc

是否有使用不可变集合构建此数组的功能方法?

我想到了:

val ingredients = List("tomatoes", "cheese") ++ List(
    if (!isVegetarian()) Some("Pepperoni") else None,
    if (shouldBeSpicy()) Some("Jalapeno") else None
).flatten

但有更好的方法吗?

6 个答案:

答案 0 :(得分:2)

这是另一种可能更接近@Antot的方式,但恕我直言更简单。

原始代码中不清楚的是isVegetarianshouldBeSpicy实际来自哪里。在这里,我假设有一个PizzaConf类来提供这些配置设置

case class PizzaConf(isVegetarian: Boolean, shouldBeSpicy: Boolean)

假设这一点,我认为最简单的方法是使用allIngredients List[(String, Function1[PizzaConf, Boolean])]类型,即存储成分和功能以检查其相应可用性的类型。鉴于buildIngredients变得微不足道:

val allIngredients: List[(String, Function1[PizzaConf, Boolean])] = List(
  ("Pepperoni", conf => conf.isVegetarian),
  ("Jalapeno", conf => conf.shouldBeSpicy)
)

def buildIngredients(pizzaConf: PizzaConf): List[String] = {
  allIngredients
    .filter(_._2(pizzaConf))
    .map(_._1)
}

或者您可以使用filter合并mapcollect,如下所示:

def buildIngredients(pizzaConf: PizzaConf): List[String] = 
  allIngredients.collect({ case (ing, cond) if cond(pizzaConf) => ing })

答案 1 :(得分:1)

您可以从完整的成分列表开始,然后过滤掉不符合条件的成分:

Set("tomatoes", "cheese", "Pepperoni", "Jalapeno")
  .filter {
    case "Pepperoni" => !isVegetarian;
    case "Jalapeno" => shouldBeSpicy; 
    case _ => true // ingredients by default
  }

用于:

val isVegetarian = true
val shouldBeSpicy = true

将返回:

Set(tomatoes, cheese, Jalapeno)

答案 2 :(得分:1)

这可以通过创建一个谓词序列来实现,该序列定义了用于过滤成分的条件。

second
`111`,`222`,`333`,`444`,`555`

答案 3 :(得分:1)

如果任何成分只需要针对一种情况进行测试,您可以这样做:

val commonIngredients = List("Cheese", "Tomatoes")
val nonVegetarianIngredientsWanted = {
  if (!isVegetarian)
    List("Pepperoni")
  else
    List.empty
}
val spicyIngredientsWanted = {
  if (shouldBeSpicy)
    List("Jalapeno")
  else
    List.empty
}
val pizzaIngredients = commonIngredients ++ nonVegetarianIngredientsWanted ++ spicyIngredientsWanted

如果您的成分经过两个类别的测试,这不起作用:例如,如果你有辣香肠,那么只有在!是素食和辣味成分的情况下才应该包括。这样做的一种方法是同时测试两种情况:

val (optionalIngredients) = {
  (nonVegetarianIngredientsWanted, spicyIngredientsWanted) match {
    case (false, false) => List.empty
    case (false, true) => List("Jalapeno")
    case (true, false) => List("Pepperoni")
    case (true, true) => List("Pepperoni, Jalapeno, Spicy Sausage")
}
val pizzaIngredients = commonIngredients ++ optionalIngredients

这可以扩展到测试任何数量的条件,当然所需的案例臂数量随着测试条件的数量呈指数级增长。

答案 4 :(得分:1)

你原来的做法还不错。我可能会坚持使用list:

val ingredients = 
  List("tomatoes", "cheese") ++
  List("Pepperoni", "Sausage").filter(_ => !isVegetarian) ++
  List("Jalapeno").filter(_ => shouldBeSpicy)

这样可以轻松添加与条件相关的更多成分(参见" Sausage"上面)

答案 5 :(得分:0)

受到其他答案的启发,我想出了类似的东西:

case class If[T](conditions: (Boolean, T)*) {
  def andAlways(values: T*): List[T] =
    conditions.filter(_._1).map(_._2).toList ++ values
}

可以像:

一样使用
val isVegetarian = false
val shouldBeSpicy = true

val ingredients = If(
   !isVegetarian -> "Pepperoni",
   shouldBeSpicy -> "Jalapeno",
).andAlways(
   "Cheese",
   "Tomatoes"
)

仍在等待更好的选择:)