我有一个List[A]
,如果给出一个相等函数(a:A, b:A) => Boolean
,那么删除重复的惯用方法是什么?我通常无法覆盖equals
A
我现在可以思考的方法是创建一个包裹class AExt
并覆盖equals
,然后
list.map(新的AExt(_))。distinct
但我想知道是否有更清洁的方式。
答案 0 :(得分:19)
有一种简单(简单)的方法可以做到这一点:
list.groupBy(_.key).mapValues(_.head)
如果您愿意,可以通过以下功能块替换_.head
来立即使用生成的地图:
sameElements => { val observedItem = sameElements.head
new A (var1 = observedItem.firstAttr,
var2 = "SomethingElse") }
为每个不同的元素返回一个新的A
。
只有一个小问题。上面的代码(list.groupBy(_.key).mapValues(_.head)
)没有很好地解释删除重复的意图。出于这个原因,拥有像distinctIn[A](attr: A => B)
或distinctBy[A](eq: (A, A) -> Boolean)
这样的函数会非常棒。
答案 1 :(得分:7)
使用misingFaktor答案中的Foo
和customEquals
:
case class Foo(a: Int, b: Int)
val (a, b, c, d) = (Foo(3, 4), Foo(3, 1), Foo(2, 5), Foo(2, 5))
def customEquals(x: Foo, y: Foo) = x.a == y.a
(Seq(a, b, c, d).foldLeft(Seq[Foo]()) {
(unique, curr) => {
if (!unique.exists(customEquals(curr, _)))
curr +: unique
else
unique
}
}).reverse
如果结果排序很重要但要删除的副本不重要,那么最好使用foldRight
Seq(a, b, c, d).foldRight(Seq[Foo]()) {
(curr, unique) => {
if (!unique.exists(customEquals(curr, _)))
curr +: unique
else
unique
}
}
答案 2 :(得分:4)
我必须说我想我会通过一个Set
的中间收集,如果你预期你的List
可能会很长测试存在 (exists
上的find
或Seq
) O(n)当然:
而不是写自定义等于;决定元素相等的属性。所以而不是:
def myCustomEqual(a1: A, a2: A) = a1.foo == a2.foo && a1.bar == a2.bar
制作密钥。像这样:
type Key = (Foo, Bar)
def key(a: A) = (a.foo, a.bar)
然后,您可以将密钥添加到Set
,以查看您之前是否遇到过这些密钥。
var keys = Set.empty[Key]
((List.empty[A] /: as) { (l, a) =>
val k = key(a)
if (keys(k)) l else { keys += k; a +: l }
}).reverse
当然,在非常短的列表的情况下,此解决方案具有更差的空间复杂性和可能更差的性能(因为您正在创建额外的对象 - 密钥)。如果您不喜欢折叠中的var
,您可能希望了解如何使用 scalaz 7中的State
和Traverse
来实现此目标
答案 3 :(得分:3)
scala> case class Foo(a: Int, b: Int)
defined class Foo
scala> val (a, b, c, d) = (Foo(3, 4), Foo(3, 1), Foo(2, 5), Foo(2, 5))
a: Foo = Foo(3,4)
b: Foo = Foo(3,1)
c: Foo = Foo(2,5)
d: Foo = Foo(2,5)
scala> def customEquals(x: Foo, y: Foo) = x.a == y.a
customEquals: (x: Foo, y: Foo)Boolean
scala> Seq(a, b, c, d) filter {
| var seq = Seq.empty[Foo]
| x => {
| if(seq.exists(customEquals(x, _))) {
| false
| } else {
| seq :+= x
| true
| }
| }
res13: Seq[Foo] = List(Foo(3,4), Foo(2,5))
答案 4 :(得分:0)
case class Foo (a: Int, b: Int)
val x = List(Foo(3,4), Foo(3,1), Foo(2,5), Foo(2,5))
def customEquals(x : Foo, y: Foo) = (x.a == y.a && x.b == y.b)
x.foldLeft(Nil : List[Foo]) {(list, item) =>
val exists = list.find(x => customEquals(item, x))
if (exists.isEmpty) item :: list
else list
}.reverse
res0:列表[Foo] =列表(Foo(3,4),Foo(3,1),Foo(2,5))
答案 5 :(得分:0)
从Scala 2.13
开始,我们可以使用新的distinctBy
方法,该方法在应用转换函数==
之后返回忽略f
确定的重复项的序列元素:< / p>
def uniqueBy [B](f:(A)=> B):列表[A]
例如:
// case class A(a: Int, b: String, c: Double)
// val list = List(A(1, "hello", 3.14), A(2, "world", 3.14), A(1, "hello", 12.3))
list.distinctBy(x => (x.a, x.b)) // List(A(1, "hello", 3.14), A(2, "world", 3.14))
list.distinctBy(_.c) // List(A(1, "hello", 3.14), A(1, "hello", 12.3))