考虑以下元组列表:
val input= List((A,B),
(C,B),
(B,A))
并假设元素(A,B)
和(B,A)
是相同的,因此是重复的,从上面的列表中消除重复的有效方式(最好是在Scala中)是什么。这意味着所需的输出是另一个列表:
val deduplicated= List((A,B),
(C,B))
提前致谢!
p.s:这不是家庭作业;)
更新:
感谢大家! " set" -solution似乎是最好的。
答案 0 :(得分:2)
您可以尝试使用set,但是您需要声明自己的元组类才能使其正常工作。
case class MyTuple[A](t: (A, A)) {
override def hashCode = t._1.hashCode + t._2.hashCode
override def equals(other: Any) = other match {
case MyTuple((a, b)) => a.equals(t._1) && b.equals(t._2) || a.equals(t._2) && b.equals(t._1)
case _ => false
}
}
val input= List(("A","B"),
("C","B"),
("B","A"))
val output = input.map(MyTuple.apply).toSet.toList.map((mt: MyTuple[String]) => mt.t)
println(output)
修改强> 特拉维斯的回答让我意识到有一个更好的方法来做到这一点。这是通过编写一个与sortBy类似的distinctBy方法。
implicit class extList[T](list: List[T]) {
def distinctBy[U](f: T => U): List[T] = {
var set = Set.empty[U]
var result = List.empty[T]
for(t <- list) {
val u = f(t)
if(!set(u)) {
result ::= t
set += u
}
}
result.reverse
}
}
println(input.distinctBy { case (a, b) => Set((a,b), (b,a)) })
答案 1 :(得分:2)
我们可以使用Set
来跟踪我们已经看过的元素,同时使用filter
来消除重复项:
def removeDuplicates[T](l: List[(T, T)]) = {
val set = scala.collection.mutable.Set[(T, T)]()
l.filter { case t@(x, y) =>
if (set(t)) false else {
set += t
set += ((y, x))
true
}
}
}
当我们找到一个我们以前没见过的元组时,我们把它和它的元素交换到集合中。
答案 2 :(得分:1)
与SpiderPig的回答相同,这里是一个不使用集合的解决方案(因为通过集合不会保留原始列表的顺序,这可能是一个烦恼)
case class MyPimpedTuple(t: Tuple2[String, String]) {
override def hashCode = t._1.hashCode + t._2.hashCode
override def equals(other: Any) = other match {
case MyPimpedTuple((a, b)) => a.equals(t._1) && b.equals(t._2) || a.equals(t._2) && b.equals(t._1)
case _ => false
}
}
val input = List[MyPimpedTuple](("A","B"), ("C","B"),("B","A"))
input.map(MyPimpedTuple(_)).distinct.map(_.t)
实施例
val input = List(("A","B"), ("C","B"),("B","A"))
//> input: List[(String, String)] = List((A,B), (C,B), (B,A))
val distinctTuples = input.map(MyPimpedTuple(_)).distinct.map(_.t)
//> distinctTuples: List[(String, String)] = List((A,B), (C,B))
答案 3 :(得分:1)
为了完整起见,可以非常简单地以纯粹功能的方式进行折叠(手动定义平等让我感到紧张,而且我不确定可变性在这里买得多):
def distinctPairs[A](xs: List[(A, A)]) = xs.foldLeft(List.empty[(A, A)]) {
case (acc, (a, b)) if acc.contains((a, b)) || acc.contains((b, a)) => acc
case (acc, p) => acc :+ p
}
这不是非常有效,因为它为每个项目搜索列表两次(并附加到列表中),但这并不难解决:
def distinctPairs[A](xs: List[(A, A)]) = xs.foldLeft(
(List.empty[(A, A)], Set.empty[(A, A)])
) {
case (current @ (_, seen), p) if seen(p) => current
case ((acc, seen), p @ (a, b)) => (p :: acc, seen ++ Set((a, b), (b, a)))
}._1.reverse
这两个实现都维持秩序。
答案 4 :(得分:1)
还要考虑依赖Map
上的唯一键,其中键是双重元素的集合,
def uniq[A](a: List[(A,A)]) = a.map( t => Set(t._1,t._2) -> t ).toMap.values
效率最高,但不够简单;适用于小型馆藏。
答案 5 :(得分:0)
是的我还建议将一个集合作为目标数据结构,因为集合查找可能比两个for循环更有效。 (对不起,我是一个clojure家伙,当然这不是clojure中最短的版本......)
(def data `(("A" "B") ("B" "C") ("B" "A")))
;;(def data `(("A" "B") ("B" "C") ("B" "A") ("C" "D") ("C" "B") ("D" "F")))
(defn eliminator [source]
(println "Crunching: " source)
(loop [s source t '#{}]
(if (empty? s) (reverse t) ;; end
(if (contains? t (list (last (first s)) (first (first s)))) ;reverse is in set !
(recur (rest s) t) ; next iteration
(recur (rest s) (conj t (first s))))))) ;; add it