Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala通过映射键分组_Scala - Fatal编程技术网

Scala通过映射键分组

Scala通过映射键分组,scala,Scala,假设我有一张这样的地图: val m: Map[Int, String] = Map(1 -> "a", 2 -> "b", 3 -> "c", 4 -> "d", ...) 假设我有一个函数:def fi:Int:String,我想将其应用于此映射的键。然后,我希望按结果fkey分组,并连接属于同一fkey组的所有字符串值 在普通scala中映射密钥时,是否有一种有效的方法来创建groupByKey。无火花。因为1和3都被映射为“奇数”,并且映射必须强制密钥的唯一性,

假设我有一张这样的地图:

val m: Map[Int, String] = Map(1 -> "a", 2 -> "b", 3 -> "c", 4 -> "d", ...)
假设我有一个函数:def fi:Int:String,我想将其应用于此映射的键。然后,我希望按结果fkey分组,并连接属于同一fkey组的所有字符串值


在普通scala中映射密钥时,是否有一种有效的方法来创建groupByKey。无火花。

因为1和3都被映射为“奇数”,并且映射必须强制密钥的唯一性,所以将丢弃一对密钥。一个简单的解决方案是将原始映射[Int,String]转换为不强制唯一性的Seq[Int,String]。例如:

m.toSeq.map {
case (k, v) => (if (k % 2 == 0) "even" else "odd", v)
}.groupBy(_._1).mapValues(
  values => values.map(_._2)
)
我建议使用foldLeft来完成此操作:

val m: Map[Int, String] = Map(1 -> "a", 2 -> "b", 3 -> "c", 4 -> "d")
m: Map[Int,String] = Map(1 -> a, 2 -> b, 3 -> c, 4 -> d)

m.foldLeft(Map.empty[String, String]) {
    case (accum, (i, s)) if i % 2 == 0 => accum + ("even" -> accum.get("even").fold(s)(existing => existing + s))
    case (accum, (i, s)) => accum + ("odd" -> accum.get("odd").fold(s)(existing => existing + s))
}

//scala.collection.immutable.Map[String,String] = Map(odd -> ac, even -> bd)
编辑:

我有很多箱子,而不仅仅是偶数和奇数。如果我想模化1000,那么有1000个不同的组。我不能在foldLeft函数中将它们全部列举为事例。有没有更通用的方法

为了推广这一点,我们需要使用比偶数或奇数更好的键。在这种情况下,听起来像是基于i的键。因此,让我们以模块10为例来避免大型控制台打印输出。让我们用一堆东西制作一张新地图:

import scala.util.Random
val testMap = (0 to 30).map(i => i -> Random.alphanumeric.filter(_.isLetter).take(Random.nextInt(3)).mkString("")).toMap
这可以使我们成为一个样本地图,其中每个条目的每个位置都有一些随机字母

实际上只有一种情况,因为我们的键控功能现在只是i本身,模块10

testMap.foldLeft(Map.empty[Int, String]) {
    case (accum, (i, s)) => accum + (i % 10 -> accum.get(i % 10).fold(s)(existing => existing + s))
}
或者,让事情变得更明显一点:

testMap.foldLeft(Map.empty[Int, String]) {
    case (accum, (i, s)) => {
        val key = i % 10
        accum + (key -> accum.get(key).fold(s)(existing => existing + s))
    }
}
下面是一个运行示例:

testMap: scala.collection.immutable.Map[Int,String] = Map(0 -> q, 5 -> Ax, 10 -> a, 24 -> AX, 25 -> "", 14 -> U, 20 -> "", 29 -> R, 1 -> hB, 6 -> ky, 28 -> ch, 21 -> dk, 9 -> v, 13 -> BR, 2 -> R, 17 -> "", 22 -> h, 27 -> "", 12 -> "", 7 -> "", 3 -> "", 18 -> "", 16 -> Qu, 11 -> XO, 26 -> gS, 23 -> "", 8 -> "", 30 -> fn, 19 -> "", 4 -> "", 15 -> Br)
res6: scala.collection.immutable.Map[Int,String] = Map(0 -> fn, 5 -> Br, 1 -> XO, 6 -> gS, 9 -> "", 2 -> "", 7 -> "", 3 -> "", 8 -> "", 4 -> U)

scala> testMap.foldLeft(Map.empty[Int, String]) {
     | case (accum, (i, s)) => accum + (i % 10 -> accum.get(i % 10).fold(s)(existing => existing + s))
     | }
res7: scala.collection.immutable.Map[Int,String] = Map(0 -> qafn, 5 -> AxBr, 1 -> hBdkXO, 6 -> kyQugS, 9 -> Rv, 2 -> Rh, 7 -> "", 3 -> BR, 8 -> ch, 4 -> AXU)

如果我正确理解了更改后的需求,那么您是在尝试将键可被给定整数整除的映射的所有值连接起来?如果是这样,这里有一种方法:

val m: Map[Int, String] = Map(1 -> "a", 2 -> "b", 3 -> "c", 4 -> "d", 5 -> "e", 6 -> "f", 7 -> "g")

def f(m: Map[Int, String], n: Int): String =
  m.filter{
    case (k, v) => k % n == 0
  }.toSeq.map{
    case (k, v) => (n, v)
  }.groupBy(_._1).
  mapValues(_.map(_._2).mkString).
  getOrElse(n, "")

f(m, 3)
// res1: String = fc

如果我了解情况,你有地图

val m: Map[K,V] = ...
…以及转换键的函数/方法

def f(k: K):J = ...  //K and J can be same type
…您希望将映射中的键分组,然后将映射中的所有值收集到它们的新组中

m.keys                 //Iterable[K]
 .toList               //List[K]
 .groupBy(f)           //Map[J,List[K]]
 .mapValues(_.map(m))  //Map[J,List[V]]
如果原始V本身就是一个列表,那么可以使用flatMap而不是map来展平结果


toList步骤是可选的。如果没有它,结果是Map[J,Iterable[V].

只是为了测试,让我们假设f是某个东西,这导致不同i的fi相同:

def f(i: Int) : String = if (i % 2 == 0) "even" else "odd" 
f: (i: Int)String
这样我们就有一些东西可以分组了

val m: Map[Int, String] = Map(1 -> "a", 2 -> "b", 3 -> "c", 4 -> "d")
映射:

m.map {case (k,v) => ((k, (f(k), v)))}
res82: scala.collection.immutable.Map[Int,(String, String)] = Map(1 -> (odd,a), 2 -> (even,b), 3 -> (odd,c), 4 -> (even,d))
分组:

scala> m.map {case (k,v) => ((k, (f(k), v)))}.groupBy {case (k,(fs, v)) => fs} 
res83: scala.collection.immutable.Map[String,scala.collection.immutable.Map[Int,(String, String)]] = Map(odd -> Map(1 -> (odd,a), 3 -> (odd,c)), even -> Map(2 -> (even,b), 4 -> (even,d)))
函数,用于从映射中拉出字符串并将其连接:

scala> def myconcat (m: Map [Int, (String, String)]) : String = m.values.foldLeft ("") {(z, s2) => z + s2._2}
myconcat: (m: Map[Int,(String, String)])String
结合上述映射和分组:

scala> m.map {case (k,v) => ((k, (f(k), v)))}.groupBy  {case (k,(fs, v)) => fs}.map (fsv => myconcat (fsv._2)) 
res90: scala.collection.immutable.Iterable[String] = List(ac, bd)

相当复杂

这对我的问题来说是可以的,但在实践中,我有很多情况,而不仅仅是偶数和奇数。如果我想模化1000,那么有1000个不同的组。我不能在foldLeft函数中将它们全部列举为事例。有更通用的方法吗?@user3685285我已经用fold@user3685285,看起来您的原始需求已发生了重大变化。请看我更新的答案。你的样本数据m是所有不同的键。你想在那里做什么?fi是否为某些i:Int返回相同的结果,并且您希望对该结果分组?