Припустимо, що маю
val dirty = List("a", "b", "a", "c")
Чи існує операція зі списком, яка повертає "a", "b", "c"
Припустимо, що маю
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
до a Seq
або a Set
(тобто, використовуючи або .toList
, .toSeq
або .toSet
учасників), щоб це працювало.
Перш ніж використовувати рішення Kitpon, подумайте про використання, Set
а не а List
, воно гарантує, що кожен елемент унікальний.
У більшості списків операцій ( foreach
, map
, filter
...) є однаковими для множин і списків, змінюючи колекцію може бути дуже легко в коді.
Використання Set в першу чергу - це правильний спосіб це зробити, звичайно, але:
scala> List("a", "b", "a", "c").toSet.toList
res1: List[java.lang.String] = List(a, b, c)
Працює. Або так само, toSet
як він підтримуєПослідовне Traversable
інтерфейс.
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
Результати продуктивності у списку 100 000 000 випадкових входів від 0 до 99:
distinct : 0.6655373s
distinctOnSorted: 0.2848134s
Хоча здається, що більш мінливий / нефункціональний підхід до програмування може бути швидшим, ніж підготовка до незмінного списку, практика показує інше. Незмінна реалізація стабільно працює краще. Я думаю, що причина полягає в тому, що 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 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 " "
}