【问题标题】:Flattening a map with a value of Option[T]展平具有 Option[T] 值的地图
【发布时间】:2018-09-13 21:45:28
【问题描述】:

我有一张地图,目前以Seq[(String, Option[T])] 之类的序列表示。我不关心地图中的None 值,因为我想做一些操作,比如对它们进行排序,这样我就可以确定要使用哪个键。我可以这样做:

val mapping: Seq[(String, Option[Foo])]

mapping.filter(_._2.isDefined)
    .sortBy(_._2.get.someInt)
    .headOption.map(_._1)

...但是必须有更好的方法来写这个。特别是,get 让我感到不舒服,因为它的值仍然是 Option[Foo] 类型,尽管其中应该没有 None 值。

有没有更好的办法?

【问题讨论】:

  • 似乎您正在对整个列表进行排序以仅获得一个元素。有maxBy
  • @AndreyTyukin:我愿意接受挑战这个问题的答案。

标签: scala


【解决方案1】:

我假设Foomapping 有以下定义:

case class Foo(someInt: Int)
val mapping: Seq[(String, Option[Foo])] = Seq(("a", Some(Foo(42))), ("b", None))

您有多种选择:

collect

collect 方法结合了模式匹配和过滤:

mapping.collect{case (s,Some(Foo(i))) => (s,i) }.sortBy(_._2).headOption.map(_._1)

for-yield

或者,你可以用for-yield达到同样的效果,它也会把所有不匹配的元素都扔掉:

(for ((s, Some(Foo(n))) <- mapping) yield(s, n)).sortBy(_._2).headOption.map(_._1)

O(N) 解与minBy

还请注意,为了提取最大值/最小值而对整个集合进行排序是不必要的:maxBy / minBy 为此:

Option(for ((s, Some(Foo(n))) <- mapping) yield (s, n))
  .filter(_.nonEmpty)
  .map(_.minBy(_._2)._1)

将整个序列包装成Option是必要的,这样我们就可以过滤掉序列为空时的情况,因此我们不会在空序列上调用minBy

【讨论】:

    【解决方案2】:

    您可以使用.collect 代替.filter

    mapping
      .collect { case (s, Some(t)) => s -> t.someInt }
      .sortBy(_._2)
      .headOption
      .map(_._1)
    

    【讨论】:

      【解决方案3】:

      如果您在Seq[Option[_]] 上使用flatten,它将删除所有None 并获取Some 的值。您对使用get 感到谨慎是对的,考虑到(尽管不太可能)出现空值的可能性,这通常有点不安全。

      val mapping: Seq[(String, Option[Foo])] = ...
      
      mapping.flatmap{case (key, maybeValue) => maybeValue.map((key,_))}
        .sortBy(_._2.someInt)
        .headOption.map(_._1)
      

      【讨论】:

        【解决方案4】:

        您可以在Option[Int] 上使用sortBy,然后使用collect 清理不需要的数据,

        scala> final case class Foo(someInt: Int)
        defined class Foo
        
        scala> val data = Seq(("zebra", Some(Foo(26))), 
                              ("banana", Some(Foo(1))), 
                              ("empty", Option.empty[Foo]))
        data: Seq[(String, Option[Foo])] = List((zebra,Some(Foo(26))), (banana,Some(Foo(1))), (empty,None))
        

        示例:

        scala> data.sortBy(_._2.map(_.someInt))
                   .collect { case (k, Some(v)) => k}
                   .headOption
        res1: Option[String] = Some(banana)
        

        【讨论】:

          【解决方案5】:

          如果你想删除 None 元素,你可以使用 collect 或 flatMap 而不是 filter。如果您想更有表现力,请避免使用 _1 或 _2 元组方法并将它们替换为案例。

          case class Foo(someInt: Int)
          
          val list = Seq("1" -> Option(Foo(1)), "2" -> Option(Foo(7)), "3" -> None, "4" -> Option(Foo(-2)))
          
          val r2 = list.flatMap { case (key, foo) => foo.map( key -> _.someInt) }.sortBy { case (_, value) => value }.headOption.map { case (key, _) => key }
          
          val r = list.collect { case (key, Some(foo)) => (key, foo.someInt) }.sortBy { case (_, value) => value}.headOption.map { case (key, _) => key }
          
          
          println(r)
          println(r2)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-01-21
            • 2021-02-01
            • 2016-11-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多