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 Set
plutôt que List
. C'est bien, mais sachez que par défaut, l' Set
interface 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 values
méthode pour obtenir une Iterable
valeur de - les clés seront toutes distinctes par construction.
groupBy
membre de scala.collection.Iterable[A]
.
scala.collection.immutable.List
a maintenant une .distinct
méthode.
Ainsi, l'appel dirty.distinct
est désormais possible sans conversion en un Set
ou Seq
.
.distinct
n'est pas défini pour scala.collection.Iterable[A]
. Donc, dans ce cas, vous devez utiliser la mise dirty
à niveau vers a Seq
ou a de Set
toute façon (c'est-à-dire en utilisant l'un ou l' autre .toList
, .toSeq
ou les .toSet
membres) pour que cela fonctionne.
Avant d'utiliser la solution de Kitpon, pensez à utiliser a Set
plutô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 toSet
comme il prend en charge leSeq Traversable
interface.
Set
outils Traversable
, non Seq
. La différence est que Seq
garantit un ordre aux éléments, alors que ce Traversable
n'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 " "
}