【问题标题】:How to get the closest value from map by given key in scala?如何通过scala中的给定键从地图中获取最接近的值?
【发布时间】:2020-10-22 09:16:31
【问题描述】:

例如,我有一个Map[Integer,String] 喜欢

val map = Map(1 -> "a", 2 -> "b", 3 -> "c", 5 -> "d", 9 -> "e", 100 -> "z")
  • 如果给定的键是 2,则预期会返回“b”。

  • 如果给定的键是 50,那么“e”和“z”应该会返回。

  • 如果给定的键为 0,则应返回“a”。

换句话说,如果键存在于Map 中,则应返回相应的值。否则应该返回最近的较小和较大键的值(如果没有其他键更小,则只返回最近的较大键的值,反之亦然)。

如何做到这一点?

【问题讨论】:

    标签: scala dictionary


    【解决方案1】:

    Map 不保留顺序,因此我建议创建一个方法:

    1. 将Map 转换为TreeMap
    2. 分别使用to(key).lastOption 和from(key).headOption 在列表中生成下/上映射条目作为选项
    3. 展平列表并提取 Map 值:

    示例代码如下:

    val map = Map(1->"a", 2->"b", 100->"z", 9->"e", 3->"c", 5->"d")
    
    def closestValues(m: Map[Int, String], key: Int): Seq[String] = {
      import scala.collection.immutable.TreeMap
      val tm = TreeMap(m.toSeq: _*)
    
      Seq( tm.to(key).lastOption, tm.from(key).headOption ).
        flatten.distinct.map{ case (k, v) => v }
    }
    
    closestValues(map, 0)
    // res1: Seq[String] = List(a)
    
    closestValues(map, 2)
    // res2: Seq[String] = List(b)
    
    closestValues(map, 50)
    // res3: Seq[String] = List(e, z)
    
    closestValues(map, 101)
    // res4: Seq[String] = List(z)
    

    更新:

    启动 Scala 2.13,TreeMap 的方法 to 和 from 分别替换为 rangeTo 和 rangeFrom。

    【讨论】:

    • 做得很好。我会在map(_._2) 调用之前移动distinct,以防近下方键和近上方键碰巧引用相同的值。
    • 好收获。谢谢@jwvh。我已经更新了答案。
    【解决方案2】:

    我的 2 美分值。

    def getClose[K](m: Map[Int,K], k: Int): Seq[K] =
      if (m.get(k).nonEmpty) Seq(m(k))
      else {
        val (below,above) = m.keys.partition(_ < k)
        Seq( if (below.isEmpty) None else Some(below.max)
           , if (above.isEmpty) None else Some(above.min)
           ).flatten.map(m)
      }
    

    【讨论】:

      【解决方案3】:

      我建议首先将Map 转换为SortedMap,因为需要考虑键的顺序。

      val map = Map(1->"a",2->"b",3->"c",5->"d",9->"e",100->"z")
      val sortedMap = SortedMap[Int, String]() ++ map
      

      之后,使用以下方法获取最接近的值。结果以 List 形式返回。

      def getClosestValue(num: Int) = {
        if (sortedMap.contains(num)) {
          List(sortedMap(num))
        } else {
          lazy val larger = sortedMap.filterKeys(_ > num)
          lazy val lower  = sortedMap.filterKeys(_ < num)
          if (larger.isEmpty) {
            List(sortedMap.last._2)
          } else if (lower.isEmpty) {
            List(sortedMap.head._2)
          } else {
            List(lower.last._2, larger.head._2)
          }
        }
      }
      

      使用以下值对其进行测试:

      println(getClosestValue(2))
      println(getClosestValue(50))
      println(getClosestValue(0))
      println(getClosestValue(101))
      

      会给

      List(b)
      List(z, e)
      List(a)
      List(z)
      

      【讨论】:

        【解决方案4】:

        这不是一个有效的解决方案,但您可以执行以下操作

        val map =Map(1->"a",2->"b",3->"c",5->"d",9->"e",100->"z")
        
        val keyset = map.keySet
        
        def getNearestValues(key: Int) : Array[String] = {
          if(keyset.contains(key)) Array(map(key))
          else{
            var array = Array.empty[String]
            val less = keyset.filter(_ < key)
            if(!less.isEmpty) array = array ++ Array(map(less.toList.sortWith(_ < _).last))
            val greater = keyset.filter(_ > key)
            if(!greater.isEmpty) array = array ++ Array(map(greater.toList.sortWith(_ < _).head))
            array
          }
        }
        

        一点点函数式的方式

        val map =Map(1->"a",2->"b",3->"c",5->"d",9->"e",100->"z")
        
        val keyset = map.keySet
        
        def getNearestValues(key: Int) : Array[String] = keyset.contains(key) match {
          case true => Array(map(key))
          case false => {
            val (lower, upper) = keyset.toList.sortWith(_ < _).span(x => x < key)
            val lowArray = if(lower.isEmpty) Array.empty[String] else Array(map(lower.last))
            val upperArray = if(upper.isEmpty) Array.empty[String] else Array(map(upper.head))
            lowArray ++ upperArray
          }
        }
        

        getNearestValues(0) 应该返回 Array(a) 和 getNearestValues(50) 应该返回 Array(e, z) 和 getNearestValues(9) 应该返回 Array(e)

        【讨论】:

          【解决方案5】:

          您可以用比上述任何建议的解决方案更小的复杂性来解决这个问题。因此,如果性能至关重要,请查看this answer。

          【讨论】:

            【解决方案6】:

            另一个 Scala 解决方案

            val m = Map(1 -> "a", 2 -> "b", 3 -> "c", 5 -> "d", 9 -> "e", 100 -> "z")
            
              List(0, 2, 50, 101).foreach { i => {
                val inp = i
                val (mn, mx) = if (m.get(inp).nonEmpty) (Map(inp -> m(inp)), Map(inp -> m(inp))) else m.partition(x => x._1 > inp)
                (mn, mx) match {
                  case (x, y) if y.isEmpty => println(m(mn.keys.min))
                  case (x, y) if x.isEmpty => println(m(mx.keys.max))
                  case (x, y) if y == x => println(m(inp))
                  case (x, y) => println(m(mn.keys.min), m(mx.keys.max))
                }
            
              }
            
              }
            

            结果:

            a
            b
            (z,e)
            z
            

            【讨论】:

              猜你喜欢
              • 2016-10-17
              • 2019-12-30
              • 2021-10-29
              • 1970-01-01
              • 1970-01-01
              • 2022-01-23
              • 1970-01-01
              • 2013-01-15
              • 1970-01-01
              相关资源
              最近更新 更多