将列表元素分组(如果它们共享一个公共元素)

时间:2019-02-27 19:48:22

标签: scala

我有一个字符串列表。元素由两个字母组成。例如,

val A = List("bf", "dc", "ab", "af")

我想收集所有具有共同字母的字母,即

"bf" and "af" share "f"

放入元组

("a", "b", "f")

另一个元组应该是

("c", "d")

所以我想返回一个看起来像这样的列表

List(List("a", "b", "f"), List("c", "d"))

我得到了预期的结果

val A= List("bf", "dc", "ab", "af")

val B= A.flatMap(x => x.split("")).distinct

B.map(y => A.map(x => if(x.contains(y)) {x} else {""}).filter(_ !="").flatMap(_.split("")).distinct.sorted).distinct

但是必须有更好的方法。

2 个答案:

答案 0 :(得分:0)

您的解决方案还不错,但是可以简化。

然后,您不必拆分字符串和flatMap。您可以将字符串列表放平。

A.map(x => if(x.contains(y)) {x} else {""}).filter(_!="") 最好写:

A.flatMap(x => if(x.contains(y)) Some(x) else None)

A.filter(_.contains(y))

但是您可以使用分区和计数来表达它,这是我的解决方案:

val a = List("bf", "dc", "ab", "af")

val b = a.flatten.distinct.sorted

b.partition(x => a.count(_.contains(x)) > 1)

答案 1 :(得分:0)

我很好奇,“ 更好”是什么意思?如果您关心以Big O表示的算法复杂性,请假设以下条件:

  • 字母很简单,设为26个字母;
  • 字符串的长度是任意的,但是比输入长度n小得多。

然后您可以在O(n)中完成此操作:

def fn(in: Iterator[String]) = {
  val a = Array.fill(26)(-1)

  for {s <- in} {
    val cl = s.toSeq.map(_ - 'a')
    val i = cl.map(a(_)).find(_ >= 0) getOrElse cl.head
    cl.foreach(a(_) = i)
  }

  a.zipWithIndex.filter(_._1 > 0).groupBy(_._1).values.map {
    _.map(_._2+'a').map(_.toChar)
  }
}

scala> fn(List("bf", "dc", "ab", "af").toIterator)
res17: Iterable[Array[Char]] = List(Array(a, b, f), Array(c, d))

返回“ 更好”。如果您想要一个漂亮的FP解决方案,那么有人可能会说我们在这里牺牲了FP,因为使用了可变变量。 这是有争议的,因为该可变变量的作用域在内部,并且该函数仍然是纯函数。