Предположим, у меня есть
val dirty = List("a", "b", "a", "c")
Есть ли операция со списком, которая возвращает "a", "b", "c"
Взгляните на ScalaDoc для Seq ,
scala> dirty.distinct
res0: List[java.lang.String] = List(a, b, c)
Обновить . Другие предлагали использовать Set
вместо List
. Это нормально, но имейте в виду, что по умолчанию Set
интерфейс не сохраняет порядок элементов. Вы можете использовать Set реализацию , которая явно имеет сохранение порядка, такие как collection.mutable.LinkedHashSet .
Map[String, File]
, где ключи являются частью интересующего имени файла. После построения карты вы можете вызватьvalues
метод для полученияIterable
значений - все ключи будут отличаться по построению.groupBy
членscala.collection.Iterable[A]
.scala.collection.immutable.List
теперь есть.distinct
метод.Таким образом, вызов
dirty.distinct
теперь возможен без преобразования вSet
илиSeq
.источник
.distinct
не определено дляscala.collection.Iterable[A]
. Так что в этом случае вам придется использовать обновлениеdirty
до aSeq
или a вSet
любом случае (то есть, используя либо.toList
,.toSeq
либо.toSet
участников), чтобы это работало.Прежде чем использовать решение Kitpon, подумайте об использовании a,
Set
а не aList
, поскольку оно гарантирует, что каждый элемент уникален.В большинстве списков операций (
foreach
,map
,filter
...) являются одинаковыми для множеств и списков, изменяя коллекцию может быть очень легко в коде.источник
Использование Set в первую очередь - это, конечно, правильный способ, но:
scala> List("a", "b", "a", "c").toSet.toList res1: List[java.lang.String] = List(a, b, c)
Работает. Или так же,
toSet
как он поддерживаетSeqTraversable
интерфейс.источник
Set
орудийTraversable
, а неSeq
. Разница в том, чтоSeq
гарантирует порядок элементов, аTraversable
не нет.Для уже отсортированных списков
Если вы хотите, чтобы отдельные элементы списка, которые, как вы знаете, уже отсортированы , как мне часто требовалось, следующее работает примерно в два раза быстрее, чем
.distinct
:def distinctOnSorted[V](seq: List[V]): List[V] = seq.foldLeft(List[V]())((result, v) => if (result.isEmpty || v != result.head) v :: result else result) .reverse
Результаты производительности в списке из 100000000 случайных Ints от 0 до 99:
distinct : 0.6655373s distinctOnSorted: 0.2848134s
Производительность с MutableList или ListBuffer
Хотя может показаться, что более изменчивый / нефункциональный подход к программированию может быть быстрее, чем добавление в неизменяемый список, практика показывает обратное. Неизменяемая реализация стабильно работает лучше. Я предполагаю, что причина в том, что scala фокусирует оптимизацию компилятора на неизменяемых коллекциях и хорошо с этим справляется. (Я приветствую других, предлагающих лучшие реализации.)
List size 1e7, random 0 to 1e6 ------------------------------ distinct : 4562.2277ms distinctOnSorted : 201.9462ms distinctOnSortedMut1: 4399.7055ms distinctOnSortedMut2: 246.099ms distinctOnSortedMut3: 344.0758ms distinctOnSortedMut4: 247.0685ms List size 1e7, random 0 to 100 ------------------------------ distinct : 88.9158ms distinctOnSorted : 41.0373ms distinctOnSortedMut1: 3283.8945ms distinctOnSortedMut2: 54.4496ms distinctOnSortedMut3: 58.6073ms distinctOnSortedMut4: 51.4153ms
Реализации:
object ListUtil { def distinctOnSorted[V](seq: List[V]): List[V] = seq.foldLeft(List[V]())((result, v) => if (result.isEmpty || v != result.head) v :: result else result) .reverse def distinctOnSortedMut1[V](seq: List[V]): Seq[V] = { if (seq.isEmpty) Nil else { val result = mutable.MutableList[V](seq.head) seq.zip(seq.tail).foreach { case (prev, next) => if (prev != next) result += next } result //.toList } } def distinctOnSortedMut2[V](seq: List[V]): Seq[V] = { val result = mutable.MutableList[V]() if (seq.isEmpty) return Nil result += seq.head var prev = seq.head for (v <- seq.tail) { if (v != prev) result += v prev = v } result //.toList } def distinctOnSortedMut3[V](seq: List[V]): List[V] = { val result = mutable.MutableList[V]() if (seq.isEmpty) return Nil result += seq.head var prev = seq.head for (v <- seq.tail) { if (v != prev) v +=: result prev = v } result.reverse.toList } def distinctOnSortedMut4[V](seq: List[V]): Seq[V] = { val result = ListBuffer[V]() if (seq.isEmpty) return Nil result += seq.head var prev = seq.head for (v <- seq.tail) { if (v != prev) result += v prev = v } result //.toList } }
Контрольная работа:
import scala.util.Random class ListUtilTest extends UnitSpec { "distinctOnSorted" should "return only the distinct elements in a sorted list" in { val bigList = List.fill(1e7.toInt)(Random.nextInt(100)).sorted val t1 = System.nanoTime() val expected = bigList.distinct val t2 = System.nanoTime() val actual = ListUtil.distinctOnSorted[Int](bigList) val t3 = System.nanoTime() val actual2 = ListUtil.distinctOnSortedMut1(bigList) val t4 = System.nanoTime() val actual3 = ListUtil.distinctOnSortedMut2(bigList) val t5 = System.nanoTime() val actual4 = ListUtil.distinctOnSortedMut3(bigList) val t6 = System.nanoTime() val actual5 = ListUtil.distinctOnSortedMut4(bigList) val t7 = System.nanoTime() actual should be (expected) actual2 should be (expected) actual3 should be (expected) actual4 should be (expected) actual5 should be (expected) val distinctDur = t2 - t1 val ourDur = t3 - t2 ourDur should be < (distinctDur) print(s"distinct : ${distinctDur / 1e6}ms\n") print(s"distinctOnSorted : ${ourDur / 1e6}ms\n") print(s"distinctOnSortedMut1: ${(t4 - t3) / 1e6}ms\n") print(s"distinctOnSortedMut2: ${(t5 - t4) / 1e6}ms\n") print(s"distinctOnSortedMut3: ${(t6 - t5) / 1e6}ms\n") print(s"distinctOnSortedMut4: ${(t7 - t6) / 1e6}ms\n") } }
источник
Вы также можете использовать рекурсию и сопоставление с образцом:
def removeDuplicates[T](xs: List[T]): List[T] = xs match { case Nil => xs case head :: tail => head :: removeDuplicates(for (x <- tail if x != head) yield x) }
источник
removeDuplicates(tail.filter(_ != head))
inArr.distinct foreach println _
источник
Алгоритмический путь ...
def dedupe(str: String): String = { val words = { str split " " }.toList val unique = words.foldLeft[List[String]] (Nil) { (l, s) => { val test = l find { _.toLowerCase == s.toLowerCase } if (test == None) s :: l else l } }.reverse unique mkString " " }
источник