Supposons que j'ai
val dirty = List("a", "b", "a", "c")
Existe-t-il une opération de liste qui renvoie "a", "b", "c"
Supposons que j'ai
val dirty = List("a", "b", "a", "c")
Existe-t-il une opération de liste qui renvoie "a", "b", "c"
Réponses:
Jetez un œil au ScalaDoc pour Seq ,
scala> dirty.distinct
res0: List[java.lang.String] = List(a, b, c)
Mettre à jour . D'autres ont suggéré d'utiliser Setplutôt que List. C'est bien, mais sachez que par défaut, l' Setinterface ne conserve pas l'ordre des éléments. Vous pouvez utiliser un ensemble implémentation qui explicitement ne préserver l' ordre, comme collection.mutable.LinkedHashSet .
Map[String, File], où les clés font partie du nom de fichier qui vous intéresse. Une fois la carte construite, vous pouvez appeler la valuesméthode pour obtenir une Iterablevaleur de - les clés seront toutes distinctes par construction.
groupBymembre de scala.collection.Iterable[A].
scala.collection.immutable.Lista maintenant une .distinctméthode.
Ainsi, l'appel dirty.distinctest désormais possible sans conversion en un Setou Seq.
.distinctn'est pas défini pour scala.collection.Iterable[A]. Donc, dans ce cas, vous devez utiliser la mise dirtyà niveau vers a Seqou a de Settoute façon (c'est-à-dire en utilisant l'un ou l' autre .toList, .toSeqou les .toSetmembres) pour que cela fonctionne.
Avant d'utiliser la solution de Kitpon, pensez à utiliser a Setplutôt que a List, cela garantit que chaque élément est unique.
Comme la plupart des opérations de liste ( foreach, map, filter, ...) sont les mêmes pour les jeux et les listes, la collecte pourrait changer très facile dans le code.
Utiliser Set en premier lieu est la bonne façon de le faire, bien sûr, mais:
scala> List("a", "b", "a", "c").toSet.toList
res1: List[java.lang.String] = List(a, b, c)
Travaux. Ou tout toSetcomme il prend en charge leSeq Traversable interface.
Setoutils Traversable, non Seq. La différence est que Seqgarantit un ordre aux éléments, alors que ce Traversablen'est pas le cas.
Si vous souhaitez que les éléments distincts d'une liste dont vous savez qu'ils sont déjà triés , comme j'en ai souvent eu besoin, ce qui suit fonctionne environ deux fois plus vite que .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
Résultats de performance sur une liste de 100 000 000 d'Ints aléatoires de 0 à 99:
distinct : 0.6655373s
distinctOnSorted: 0.2848134s
Alors qu'il semblerait qu'une approche de programmation plus modifiable / non fonctionnelle puisse être plus rapide que le préfixe à une liste immuable, la pratique montre le contraire. L'implémentation immuable fonctionne toujours mieux. Je suppose que la raison est que scala concentre ses optimisations de compilateur sur des collections immuables et fait du bon travail. (J'encourage les autres à soumettre de meilleures implémentations.)
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
Implémentations:
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
}
}
Tester:
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")
}
}
La manière algorithmique ...
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 " "
}