Scala 替代附加到列表的一系列 if 语句



我在 Scala 中有一个Seq[String],如果Seq包含某些String,我会将相关消息附加到另一个列表中。

有没有一种更"scalaesque"的方法可以做到这一点,而不是像下面那样附加到列表中的一系列 if 语句?

val result = new ListBuffer[Err]()
val malformedParamNames = // A Seq[String]
if (malformedParamNames.contains("$top")) result += IntegerMustBePositive("$top")
if (malformedParamNames.contains("$skip")) result += IntegerMustBePositive("$skip")
if (malformedParamNames.contains("modifiedDate")) result += FormatInvalid("modifiedDate", "yyyy-MM-dd")
...
result.toList

如果你想使用一些 scala 迭代糖,我会使用

sealed trait Err
case class IntegerMustBePositive(msg: String) extends Err
case class FormatInvalid(msg: String, format: String) extends Err
val malformedParamNames = Seq[String]("$top", "aa", "$skip", "ccc", "ddd", "modifiedDate")
val result = malformedParamNames.map { v =>
  v match {
    case "$top" => Some(IntegerMustBePositive("$top"))
    case "$skip" => Some(IntegerMustBePositive("$skip"))
    case "modifiedDate" => Some(FormatInvalid("modifiedDate", "yyyy-MM-dd"))
    case _ => None
  }
}.flatten

result.toList

请注意,如果你要求斯卡拉式的做事方式,有很多可能性。

地图功能与扁平化相结合,可以通过使用平面图来简化

sealed trait Err
case class IntegerMustBePositive(msg: String) extends Err
case class FormatInvalid(msg: String, format: String) extends Err
val malformedParamNames = Seq[String]("$top", "aa", "$skip", "ccc", "ddd", "modifiedDate")
val result = malformedParamNames.flatMap {
  case "$top" => Some(IntegerMustBePositive("$top"))
  case "$skip" => Some(IntegerMustBePositive("$skip"))
  case "modifiedDate" => Some(FormatInvalid("modifiedDate", "yyyy-MM-dd"))
  case _ => None
}

result

在保持可读性的同时,我能想到的大多数"比例"版本是:

  val map = scala.collection.immutable.ListMap(
    "$top" -> IntegerMustBePositive("$top"),
    "$skip" -> IntegerMustBePositive("$skip"),
    "modifiedDate" -> FormatInvalid("modifiedDate", "yyyy-MM-dd"))
 val result = for {
   (k,v) <- map
   if malformedParamNames contains k
 } yield v 
 //or 
 val result2 = map.filterKeys(malformedParamNames.contains).values.toList

Benoit 可能是最像 scala 式的这样做的方式,但根据稍后谁将阅读代码,您可能需要不同的方法。

// Some type definitions omitted
val malformations = Seq[(String, Err)](
  ("$top", IntegerMustBePositive("$top")),
  ("$skip", IntegerMustBePositive("$skip")),
  ("modifiedDate", FormatInvalid("modifiedDate", "yyyy-MM-dd")
)

如果您需要一个列表并且顺序很重要:

val result = (malformations.foldLeft(List.empty[Err]) { (acc, pair) =>
  if (malformedParamNames.contains(pair._1)) {
    pair._2 ++: acc // prepend to list for faster performance
  } else acc
}).reverse // and reverse since we were prepending
如果顺序不

重要(尽管如果顺序不重要,您可以考虑使用集合而不是列表):

val result = (malformations.foldLeft(Set.empty[Err]) { (acc, pair) =>
  if (malformedParamNames.contains(pair._1)) {
    acc ++ pair._2
  } else acc
}).toList // omit the .toList if you're OK with just a Set

如果重复 if 中的谓词更复杂/更不均匀,那么畸形的类型可能需要改变,就像响应改变时一样,但基本模式非常灵活。

在此解决方案中,我们定义了一个映射列表,这些映射成对接受您的 IF 条件和 THEN 语句,我们迭代输入的列表并将更改应用到它们匹配的位置。

//                     IF              THEN
case class Operation(matcher :String, action :String)
def processInput(input :List[String]) :List[String] = {
    val operations = List(
        Operation("$top", "integer must be positive"),
        Operation("$skip", "skip value"),
        Operation("$modify", "modify the date")
    )
    input.flatMap { in =>
        operations.find(_.matcher == in).map { _.action }
    }
}
println(processInput(List("$skip","$modify", "$skip")));

细分

operations.find(_.matcher == in)    // find an operation in our
                                    // list matching the input we are
                                    // checking. Returns Some or None
.map { _.action }                   // if some, replace input with action
                                    // if none, do nothing
input.flatMap { in =>               // inputs are processed, converted
                                    // to some(action) or none and the
                                    // flatten removes the some/none
                                    // returning just the strings.

最新更新