【问题标题】:Grouping keys based on values in scala map根据scala map中的值对键进行分组
【发布时间】:2018-10-26 02:01:43
【问题描述】:

假设我有一个这样的 scala 地图 -

Map[String, List[String]] = Map(
"A"->List(1,2,3),
"B"->List(2,4),
"C"->List(4,5),
"D"->List(7,8,9),
"E"->List(8,9),
"F"->List(4,5,7),
"G"->List(1,3,2)
)

我想对键进行分组,使它们的交集不为空。例如结果应该是 -

Map(
List("A","B","G")->List(2),
List("C","F")->List(4,5),
List("D","E")->List(8,9)
)

请注意,可以有多个输出,例如可以有 List("B","C")->List(4)。我只需要选择任何人,只要所有的键都存在并且键不应该重复,例如 - 'B' 和 'C' 已经出现在地图中,所以稍后即使我们仍然找到任何交叉对是不为空,例如我们找到了“B”和“C”,我们不应该添加它。 我解决这个问题的方法包括将值列表转换为一个集合,然后遍历 map 的所有条目,使其不断将其值与下一个值相交,如果交集不为空,我们将对键进行分组。

但我想知道是否有其他方法可以做到这一点。

编辑-

关键是键列表应该很大,并且它的值应该至少有一个元素。

【问题讨论】:

  • 编辑了问题。请检查。

标签: scala collections


【解决方案1】:

您可以按照这种方法获得所需的输出。

假设您有以下格式的输入。

val map1: Map[String, List[Int]] = Map(
  "A"->List(1,2,3),
  "B"->List(2,4),
  "C"->List(4,5),
  "D"->List(7,8,9),
  "E"->List(8,9),
  "F"->List(4,5,7)
)

为了获得唯一值,我们将值列表转换为集合,以便获得值之间的交集

val map2 = map1.map(x => (x._1,x._2.toSet))

    val result: Map[List[String], Set[Int]] = 
for(a <- map2;
    b <- map2 if( (a._1!=b._1)   && (a._2 intersect b._2).nonEmpty))
yield( List(a._1,b._1) -> (a._2 intersect b._2) )

这个函数会给你结果

result: Map[List[String],Set[Int]] = Map(List(D, F) -> Set(7), List(C, F) -> Set(4, 5), List(E, D) -> Set(8, 9), List(B, C) -> Set(4), List(B, A) -> Set(2), List(A, B) -> Set(2), List(F, C) -> Set(4, 5), List(C, B) -> Set(4), List(D, E) -> Set(8, 9), List(F, D) -> Set(7), List(F, B) -> Set(4), List(B, F) -> Set(4))

如果您对此方法有任何疑问,请告诉我。谢谢

【讨论】:

  • 答案在某种程度上给出了预期的输出。结果中获取的键列表不应包含重复值。例如 - 现在我们得到 - List(D, F) 和 List(C, F) 都有'F'
【解决方案2】:

我将 Map 的值转换为 List[Int] 只是为了更容易加载到 IDE 中。这很容易撤消。

val m :Map[String, List[Int]] = Map(
  "A"->List(1,2,3),
  "B"->List(2,4),
  "C"->List(4,5),
  "D"->List(7,8,9),
  "E"->List(8,9),
  "F"->List(4,5,7),
  "G"->List(1,3,2))

然后我 fold 遍历按键,看看是哪些 intersect

m.keys.foldLeft(List.empty[(List[String],List[Int])]){case (acc,k) =>
  val (not, has) = acc.partition(_._2.intersect(m(k)).isEmpty)
  has.headOption.fold((List(k),m(k)) :: acc){case (s,v) =>
    (k::s, v.intersect(m(k))) :: has.tail ::: not
  }
}.toMap
//res0: Map[List[String],List[Int]] = Map(List(D, E) -> List(8, 9)
//                                       ,List(C, F) -> List(4, 5)
//                                       ,List(B, G, A) -> List(2))

这有点“蛮力”的方法,为partition() 获取一次交集,并在构建累加器的新条目时再次获取交叉点,原始地图m 被多次取消引用每个k价值。


解释

从 Map 的键中,我正在构建 List(List[String],List[Int]) 元组。对于每个 Map 键,累积列表在与新键的值有交集的元素 (has) 和不存在交集的元素 (not) 之间进行分区。

我只从has 组中获取第一个元素。如果没有,则该键及其值被添加到结果中,否则该键被添加到 has.head 键列表中,并且值列表被调整以反映当前的交集。该新元素被添加到has.tail(忽略交叉点)之前,并与not 列表中的所有内容结合在一起。

【讨论】:

  • 成功了。谢谢。你能解释一下吗?
猜你喜欢
  • 2016-09-11
  • 1970-01-01
  • 2012-12-16
  • 2015-03-29
  • 1970-01-01
  • 2018-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多