将字符串列表转换为Map[String,List]

10

我正在尝试将List("a,1" , "b,2" , "c,3" , "a,2" , "b,4")转换为类型scala.collection.immutable.HashMap[String, java.util.List[String]],并带有以下值:

a -> 1,2
b -> 2,4
c -> 3

因此,每个键都包含其值的列表。

以下是我的代码:

object ConvertList extends Application {

  var details = new scala.collection.immutable.HashMap[String, java.util.List[String]]

  val strList = List("a,1" , "b,2" , "c,3" , "a,2" , "b,4")

  //Get all values
  val getValue : Function1[String, String] = { a => a.split(",")(1) }
  val allValues : List[String] = strList map getValue

  //get unique values
  val uniqueValues = allValues.toSet[String]

  //Somehow map each unique value to a value in the original List....
  println(uniqueValues)

  println(strList.flatten)
  //userDetails += "1" -> List("a","b",


}

这个转换该如何执行?


你对不可变的哈希表要求严格吗? - om-nom-nom
@om-nom-nom 不好意思,我只是想避免使用命令式解决方案。 - blue-sky
5个回答

15
strList.map(s => (s(0).toString,s(2).toString))
       .groupBy(_._1)
       .mapValues(_.map(_._2))

输出:

Map[String,List[String]] = Map(b -> List(2, 4), a -> List(1, 2), c -> List(3))

你能解释一下 's(0).toString,s(2).toString)' 吗?它是将每个字符串元素映射到一个 String,String 元组吗? - blue-sky
好答案。在字符串上进行索引真的使得这个解决方案更加简洁。点赞! - Brian
2
是的,它将每个字符串映射到一对('第一个字母','第三个字母')中(因此"a,1"变为("a","1"))。如果您编写一个函数formatString(s),并使用strList.map(formatString).groupBy(...)更好地处理特殊情况(例如string.length < 3等),那么您可以(也应该这样做,如果这段代码不仅仅是脑筋急转弯)。 - Marth

3

列表不会按照相同的顺序排列,但通常这是一个相当可行的问题:

// for a sake of pithiness
type M = Map[String,List[String]] 
def empty: M = Map.empty.withDefaultValue(Nil)

@annotation.tailrec
def group(xs: List[String], m: M = empty): M = xs match {
    case Nil     => m
    case h::tail => 
      val Array(k,v) = h.split(",")
      val updated = v::m(k)
      combine(tail, m + (k -> updated))
}

3

已经有很多看法了,但是像Marth提出的类似方案呢:

import scala.collection.JavaConverters._

val strList = List("a,1" , "b,2" , "c,3" , "a,2" , "b,4")

strList.map(_.split(',')).collect { 
  case Array(key, value) => key -> value 
}.groupBy(_._1).mapValues(_.map(_._2).asJava)

这个过程非常依赖于函数式编程,并最终得到一个类型为Map[String, java.util.List[String]]Map。它不仅仅是在输入字符串中取固定位置,而是在逗号处进行分割(想象一下有超过9的数字,需要多于一个数字)。此外,如果分割后有多个值,则collect方法会过滤掉它们。

1
scala> List("a,1" , "b,2" , "c,3" , "a,2" , "b,4")
res0: List[String] = List(a,1, b,2, c,3, a,2, b,4)

scala> res0.groupBy(xs => xs.split(",")(0)).mapValues(xs => xs.flatMap(xs => xs.toCharArray.filter(_.isDigit)))
res2: scala.collection.immutable.Map[String,List[Char]] = Map(b -> List(2, 4), a -> List(1, 2), c -> List(3))

使用groupBy可以使这个过程变得简单,因为你需要一个MapgroupByList的每个元素分割成,并取第一个作为键。这会得到以下结果:scala.collection.immutable.Map[String,List[String]] = Map(b -> List(b,2, b,4), a -> List(a,1, a,2), c -> List(c,3))。从这里开始,只需处理每个值列表中的数字即可。
这将返回一个Map[String, List[Char]]。如果想要返回scala.collection.immutable.HashMap[String, java.util.List[String]],还需要做一些工作,但那是容易的部分。

0
从Scala 2.13开始,我们可以使用新的groupMap方法,它(顾名思义)是对分组和映射的一次性等效操作:
// val strList = List("a,1" , "b,2" , "c,3" , "a,2" , "b,4")
strList.map(_.split(",")).groupMap(_(0))(_(1))
// Map("b" -> List(2, 4), "a" -> List(1, 2), "c" -> List(3))

这个代码:

  • 将每个字符串拆分(生成List(Array(a, 1), Array(b, 2), ...)

  • 根据它们的第一部分(_(0))对元素进行分组(groupMap的一部分)

  • 将分组后的元素映射到它们的第二部分(_(1))(groupMap的映射部分)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接