在scala中,如何通过给定的键从映射中获取最接近的值?

在scala中,如何通过给定的键从映射中获取最接近的值?,scala,dictionary,Scala,Dictionary,例如,我有一个映射[Integer,String]像 val map = Map(1 -> "a", 2 -> "b", 3 -> "c", 5 -> "d", 9 -> "e", 100 -> "z") 如果给定的键为2,则“b”将返回 如果给定的键为50,则“e”和“z”将返回 如果给定的键为0,则“a”应返回 换句话说,如果映射中存在该键,则应返回相应的值。否则,应返回最近的较小和较大键的值(如果没有其他键较小,则应仅返回最近的较大键的值,反之亦然

例如,我有一个
映射[Integer,String]

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

  • 如果给定的键为50,则“e”和“z”将返回

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

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


如何做到这一点

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

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(键:Int):数组[字符串]={
if(keyset.contains(key))数组(map(key))
否则{
变量数组=数组。空[字符串]
val less=键集过滤器(u<键)
如果(!less.isEmpty)array=array++array(map(less.toList.sortWith(<\uu).last))
val更大=键集过滤器(>key)
如果(!greater.isEmpty)数组=数组++数组(映射(greater.toList.sortWith(<\uu).head))
排列
}
}
一点功能性方式

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
  }
}
val-map=map(1->“a”、2->“b”、3->“c”、5->“d”、9->“e”、100->“z”)
val keyset=map.keyset
def getNearestValues(key:Int):数组[String]=keyset.contains(key)匹配{
case true=>数组(映射(键))
大小写错误=>{
val(下,上)=键集.toList.sortWith(x<键)
val lowArray=if(lower.isEmpty)Array.empty[String]else数组(map(lower.last))
val upperArray=if(upper.isEmpty)Array.empty[String]else数组(map(upper.head))
lowArray++upperArray
}
}

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

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

val map = Map(1->"a",2->"b",3->"c",5->"d",9->"e",100->"z")
val sortedMap = SortedMap[Int, String]() ++ map
之后,使用以下方法获得最接近的值。结果将作为列表返回

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)
    }
  }
}
将给予

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

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

  • Map
    转换为
    TreeMap
  • 分别使用
    to(key).lastpoption
    from(key).headOption
    在列表中生成上下地图条目作为选项
  • 展平列表并提取贴图值:
  • 示例代码如下:

    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

    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)
      }
    
    def getClose[K](m:Map[Int,K],K:Int):Seq[K]=
    if(m.get(k).非空)Seq(m(k))
    否则{
    val(上下)=m.keys.partition(uk
    您可以解决这个问题,其复杂性比上述任何建议的解决方案都要小。因此,如果性能至关重要,请检查。

    另一个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
    

    干得好。我会将
    distinct
    移动到
    映射(u._2)
    调用之前,以防near-bellow键和near-bellow键引用相同的值。捕捉得好。谢谢@jwvh。我已经更新了答案。
    a
    b
    (z,e)
    z