根据scala映射中的值对键进行分组

时间:2018-05-16 06:21:58

标签: scala collections

假设我是这样的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',我们不应该添加它。 我解决这个问题的方法包括将值列表转换为集合,然后遍历映射的所有条目,使其继续将其值与下一个值相交,如果交集不为空,我们将对键进行分组。

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

编辑 -

重点是密钥列表应该是大的,其值应该至少有一个元素。

2 个答案:

答案 0 :(得分: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

<强>解释

从地图键中我建立List(List[String],List[Int])元组。对于每个Map键,累积列表在那些与新键值(has)相交的元素和没有交集(not)的元素之间进行分区。

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

答案 1 :(得分:0)

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

假设您输入了以下格式。

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))

如果您对此方法有任何疑问,请与我们联系。感谢

相关问题