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

时间:2018-03-14 02:18:45

标签: scala dictionary

例如,我有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中,则应返回相应的值。否则,应返回最接近的较小和较大键的值(在没有其他键较小的情况下,只应返回最近的较大键的值,反之亦然)。

如何实现这一目标?

6 个答案:

答案 0 :(得分:6)

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

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

    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)
    

答案 1 :(得分: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)
  }

答案 2 :(得分:1)

这不是一个有效的解决方案,但你可以做类似下面的事情

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)

答案 3 :(得分:1)

我建议先将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 :(得分:0)

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

答案 5 :(得分:0)

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