scala - 列表中的 groupBy 作为 LinkedHashMap 而不是 Map

标签 scala list sorting hashmap

我正在使用 Scala 处理 XML,并将 XML 转换为我自己的数据结构。目前,我使用的是普通 Map实例来保存(子)元素,但是,XML 中元素的顺序会以这种方式丢失,并且我无法重现原始 XML。

因此,我想使用 LinkedHashMap实例而不是 Map ,但是我正在使用 groupBy在节点列表上,创建一个 Map :

例如:

  def parse(n:Node): Unit = 
  {
    val leaves:Map[String, Seq[XmlItem]] =
      n.child
        .filter(node => { ... })
        .groupBy(_.label)
        .map((tuple:Tuple2[String, Seq[Node]]) =>
        {
          val items = tuple._2.map(node =>
          {
            val attributes = ...

            if (node.text.nonEmpty)
              XmlItem(Some(node.text), attributes)
            else
              XmlItem(None, attributes)
          })

          (tuple._1, items)
        })

      ...
   }

在这个例子中,我想要 leaves类型为 LinkedHashMap保留n.child的顺序.我怎样才能做到这一点?

注意:我按标签/标记名分组,因为元素可以多次出现,并且对于每个标签/标记名,我在我的数据结构中保留了一个元素列表。

解决方案
正如@jwvh 所回答的,我正在使用 foldLeft作为 groupBy 的替代品.另外,我决定和LinkedHashMap一起去而不是 ListMap .
  def parse(n:Node): Unit = 
  {
    val leaves:mutable.LinkedHashMap[String, Seq[XmlItem]] =
      n.child
        .filter(node => { ... })
        .foldLeft(mutable.LinkedHashMap.empty[String, Seq[Node]])((m, sn) =>
        {
          m.update(sn.label, m.getOrElse(sn.label, Seq.empty[Node]) ++ Seq(sn))
          m
        })
        .map((tuple:Tuple2[String, Seq[Node]]) =>
        {
          val items = tuple._2.map(node =>
          {
            val attributes = ...

            if (node.text.nonEmpty)
              XmlItem(Some(node.text), attributes)
            else
              XmlItem(None, attributes)
          })

          (tuple._1, items)
        })

最佳答案

得到粗略等价于 .groupBy()ListMap你可以fold在你的收藏。问题是ListMap保留元素添加时的顺序,而不是它们遇到时的顺序。

import collection.immutable.ListMap

List('a','b','a','c').foldLeft(ListMap.empty[Char,Seq[Char]]){
  case (lm,c) => lm.updated(c, c +: lm.getOrElse(c, Seq()))
}
//res0: ListMap[Char,Seq[Char]] = ListMap(b -> Seq(b), a -> Seq(a, a), c -> Seq(c))

要解决此问题,您可以 foldRight而不是 foldLeft .结果是遇到的元素的原始顺序(从左到右扫描)但相反。
List('a','b','a','c').foldRight(ListMap.empty[Char,Seq[Char]]){
  case (c,lm) => lm.updated(c, c +: lm.getOrElse(c, Seq()))
}
//res1: ListMap[Char,Seq[Char]] = ListMap(c -> Seq(c), b -> Seq(b), a -> Seq(a, a))

这不一定是坏事,因为 ListMap使用 last 效率更高和 init ops,O(1),与 head 相比和 tail操作,O(n)。

处理 ListMap在原始的从左到右的顺序中,您可以 .toList.reverse它。
List('a','b','a','c').foldRight(ListMap.empty[Char,Seq[Char]]){
  case (c,lm) => lm.updated(c, c +: lm.getOrElse(c, Seq()))
}.toList.reverse
//res2: List[(Char, Seq[Char])] = List((a,Seq(a, a)), (b,Seq(b)), (c,Seq(c)))

关于scala - 列表中的 groupBy 作为 LinkedHashMap 而不是 Map,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55454933/

相关文章:

java - Neo4j:哪个内部模块负责验证密码的 WHERE 条件?

javascript - sencha touch 改变特定列表项的颜色

Java 不区分大小写的本地化排序

c++ - 适用于存储和计算最高得分 K 项的数据结构

javascript - 更改排序顺序时更新键值的最短方法是什么

scala - 使用 Gatling 进行可变斜升

postgresql - 在 scala 框架中支持 PostgreSQL 特定的 array_agg 函数?

scala - 将 F 有界类型表示为抽象类型成员

list - Haskell:如何 append 到元组列表列表?

python (pandas) - 连接列表时出现 TypeError : must be str, 未列出