【问题标题】:Log warnings in pattern match without returning Any在模式匹配中记录警告而不返回任何
【发布时间】:2020-03-04 01:22:59
【问题描述】:

我有一个可迭代的数组,我正在尝试将其转换为案例类,并且我正在映射它们以这样做。如果数组不可转换为案例类,我想记录警告并继续映射。但是,当我实现警告时,返回类型从Iterable[MyCaseClass] 变为Iterable[Any],这不是我想要的。例如:

case class MyCaseClass(s1: String, s2: String)
object MyCaseClass {
  def apply(sa: Array[String]) = new MyCaseClass(sa(0), sa(1))
}

val arrayIterable: Iterable[Array[String]] = Iterable(Array("a", "b"), Array("a", "b", "c"))

def badReturnType(): Iterable[Any] = { // Iterable[Any] is undesireable
  arrayIterable map {
    case sa: Array[String] if sa.length == 2 => MyCaseClass(sa)
    case _ => println("something bad happened!") // but warnings are good
  }
}

def desiredReturnType(): Iterable[MyCaseClass] = { // Iterable[MyCaseClass] is desireable
  arrayIterable map {
    case sa: Array[String] if sa.length == 2 => MyCaseClass(sa)
    // but no warnings if things go wrong!
  }
}

我想写一个满足以下条件的函数:

  1. 映射Iterable,将每个元素转换为MyCaseClass
  2. 当我得到一个无法转换为MyCaseClass 的数组时记录警告
  3. 记录警告后,传入匹配条件的数组将被忽略/丢弃
  4. 返回类型应为Iterable[MyCaseClass]。

我怎样才能满足这些条件?

【问题讨论】:

    标签: scala


    【解决方案1】:

    考虑使用List 代替Array,并尝试将Option 与flatMap 结合使用

    l flatMap {
      case e if e.length == 2 => Some(MyCaseClass(e))
      case e => println(s"$e is wrong length"); None
    }
    

    另一种方法是partitionMap

    val (lefts, rights) = l.partitionMap {
      case e if e.size == 2 => Right(MyCaseClass(e))
      case e  => Left(s"$e is wrong length")
    }
    
    lefts.foreach(println)
    rights
    

    【讨论】:

    • partitionsMap 看起来不错,是 2.13 的新功能吗?
    • @LuisMiguelMejíaSuárez 是的,stackoverflow.com/a/54275748/5205022
    • @LuisMiguelMejíaSuárez 我最喜欢的两个活跃的 SO 回答者。
    【解决方案2】:

    你可以这样做:

    final case class MyCaseClass(s1: String, s2: String)
    
    def parse(input: Array[String]): Either[String, MyCaseClass] = input match  {
      case Array(s1, s2) => Right(MyCaseClass(s1, s2))
      case _ => Left(s"Bad input: ${input.mkString("[", ", ", "]")}")
    }
    
    def logErrors(validated: Either[String, _]): Unit = validated match {
      case Left(error) => println(error)
      case Right(_)    => ()
    }
    
    def validateData(data: IterableOnce[Array[String]]): List[MyCaseClass] =
      data
        .iterator
        .map(parse)
        .tapEach(logErrors)
        .collect {
          case Right(value) => value
        }.toList
    

    你可以这样使用:

    val arrayIterable = Iterable(Array("a", "b"), Array("a", "b", "c"))
    validateData(arrayIterable)
    // Bad input: [a, b, c]
    // res14: List[MyCaseClass] = List(MyCaseClass("a", "b"))
    

    【讨论】:

    • 恕我直言 tapEach 平局,所以我对这两个答案都投了赞成票,但有人应该注意到避免中间集合的好处。
    • @som-snytt 有时我们甚至比赛 :) stackoverflow.com/a/59903201/5205022
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-21
    • 2021-01-26
    • 1970-01-01
    • 2018-01-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多