如何在 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的语法是什么?

如何在 Scala 中将 Map 序列化为 JSON?

如何在Scala中将rdd对象转换为数据框

flutter - 如何在Dart/Flutter中将某些元素从一个Map复制到新Map中?

如何在 Scala 中将 Spark DataFrames 一一添加到 Seq()

在 Scala 中将 Struct 数据类型转换为 Map 数据类型