如何在 Scala 中将元素添加到 Map 中,其中键是字符串,值是 List[String]
Posted
技术标签:
【中文标题】如何在 Scala 中将元素添加到 Map 中,其中键是字符串,值是 List[String]【英文标题】:How to add the elements into the Map where key is String and Value is List[String] in Scala 【发布时间】:2021-07-01 05:00:40 【问题描述】:我有a text file,其中包含有关发件人和消息的信息。格式为发件人,消息。
我已将文件加载到 RDD 中并用“,”分割它们,并创建了一个键值对,其中键是发件人,值是消息RDD[(String,String)]
。
然后,我做了一个groupByKey()
来根据发件人对邮件进行分组,我得到了一个RDD[(String,Iterable[String])]
。
Array[(String, Iterable[String])] = Array((Key,CompactBuffer(value1,value2,value3,....))
现在,我想迭代 value 部分,并将值一个一个地存储到 List 中,所以我创建了一个空 Map,其中 key 是 String,value 是 List[String]
首先我应该检查 Map 是否为空,如果它是空的,那么我应该将第一个值添加到 Map 中存在的 List 中。
以下是我尝试过但我无法做到的,当我检查地图时它显示无。
import org.apache.spark.SparkConf, SparkContext
import scala.collection.mutable.ListBuffer
object Demo
def main(args: Array[String]): Unit =
val conf = new SparkConf().setMaster("local").setAppName("My App")
val sc = new SparkContext(conf)
val inputFile = "D:\\MyData.txt"
val data = sc.textFile(inputFile)
val data2 = data.map(line => val arr = line.split(",");
(arr(0),arr(1)))
val grpData = data2.groupByKey()
val myMap = scala.collection.mutable.Map.empty[String,List[String]]
for(value <- grpData.values)
val list = ListBuffer[String]()
if(myMap.isEmpty)
list += value
myMap.put("G1", list.toList)
在 for 循环中,我给出了 grpData.values 因为我只需要值部分。我不希望我文件中的任何密钥作为发件人。我只是使用它们根据发件人对消息进行分组,但在 Map[String,List[String]] 中,我的键应该是 Group1、Group2 等等。该值是我将从 CompactBuffer 中一一获取的消息。
首先,我应该检查地图是否为空,如果为空,我应该将第一条消息添加到地图中存在的列表中。键应该是“Group1”,值应该是应该存储在 List[String] 中的消息。
对于第二次迭代,Map 不会为空,那么条件将转到 else 部分,在 else 部分我应该使用 lavenshtein 距离算法来比较消息。这里第一条消息已经添加到列表中,现在我应该从 Map 获取第一条消息,并使用 lavenshtein 距离算法将其与第二条消息进行比较,阈值为 70%。如果 2 条消息达到阈值,那么我应该将第二条消息添加到列表中,如果没有,我应该将第二条消息添加到单独的列表中,并将键名保留为“G2”,依此类推。
【问题讨论】:
您能否像示例输入一样添加预期输出,以便我们了解您要做什么? 当然,我会这样做的。 命令式解释是不够的。您假设您采取的步骤是正确的或最理想的。向我们展示示例输入和预期结果。 不,我刚刚解释了我的要求,我从未假设我是正确的。我已经添加了我的示例输入,请检查。我没有任何输出样本,我已经解释过我应该使用 lavenshtein 距离算法找到类似的消息并将它们存储在列表中。谢谢 不要解释要采取的步骤。想出一个微不足道的输入和一个预期的输出。 【参考方案1】:您可以使用aggregateByKey
获取每个键的字符串组合列表:
val data = sc.textFile(inputFile)
val data2 = data.map(line => val arr = line.split(","); (arr(0),arr(1)))
val result = data2.aggregateByKey(List[String]())(_ :+ _, _ ++ _)
// or to prepend rather than append,
// val result = data2.aggregateByKey(List[String]())((x, y) => y :: x, _ ++ _)
如果您希望结果为Map
,您可以这样做
val resultMap = result.toMap
【讨论】:
非常感谢,实际上我的要求略有不同,这就是我使用 Map[String, List[String]] 的原因。我将编辑我的问题并简要解释一下,请检查并帮助我。 不建议使用:+
附加到列表中,因为它具有线性时间复杂度。
@yangzai 你有什么更好的建议吗?
在列表中添加元素 o(1) 操作【参考方案2】:
我假设您正在尝试基于某个距离函数进行聚类,这可能是您正在寻找的:
def isWithinThreshold(s1: String, s2: String): Boolean = ???
//2 sets are grouped when there exist elements in a both sets that are closed to each other
def combine(acc: Vector[Vector[String]], s: Vector[String]) =
val (near, far) = acc.partition(_.exists(str => s.exists(isWithinThreshold(str, _))))
near.fold(s)(_ ++ _) +: far
val preClusteringGroups = grpData.values.map(_.toVector) //this is already pre-grouped with with the key from data2 (`arr(0)`)
val res = preClusteringGroups.aggregate(Vector.empty[Vector[String]])(combine, case (v1, v2) =>
(v1 ++ v2).foldLeft(Vector.empty[Vector[String]])(combine)
).zipWithIndex.map case (v, i) => s"G$i" -> v .toMap //.mapValues(_.toList) if you actually need a list
preClusteringGroups
基于 grpData
,它已经由原始密钥预先分组,可能无法满足您的距离要求。如果是这种情况,请重新定义 preClusteringGroups
:
val preClusteringGroups = data2.values.map(Vector(_))
【讨论】:
以上是关于如何在 Scala 中将元素添加到 Map 中,其中键是字符串,值是 List[String]的主要内容,如果未能解决你的问题,请参考以下文章
将元素添加到scala.collection.mutable.Map的语法是什么?
flutter - 如何在Dart/Flutter中将某些元素从一个Map复制到新Map中?