Scala'da bir listeden kopyaları nasıl kaldırırım?


96

Varsayalım ki bende

val dirty = List("a", "b", "a", "c")

"A", "b", "c" döndüren bir liste işlemi var mı

Yanıtlar:


177

Seq için ScalaDoc'a bir göz atın ,

scala> dirty.distinct
res0: List[java.lang.String] = List(a, b, c)

Güncelle . Diğerleri Setyerine kullanmayı önerdiler List. Sorun değil, ancak varsayılan olarak Setarayüzün öğe sırasını korumadığını unutmayın. Açıkça bir ayarla uygulamasını kullanmak isteyebilirsiniz yok gibi, düzeni korumak collection.mutable.LinkedHashSet .


2
Ya bir dosya listeniz varsa ve dosya adının bir bölümü gibi bir şey üzerinde karşılaştırma yapmanız gerekiyorsa?
ozon

4
@ozone İlginç soru. Belki en kolay yolu yeni bir yaratmaktır harita Çeşidi Map[String, File]tuşlarını kullanarak ilgili dosya adının parçasıdır. Harita oluşturulduktan sonra, valuesbir Iterabledeğer elde etmek için yöntemi çağırabilirsiniz - anahtarların tümü yapı itibariyle farklı olacaktır.
Kipton Barros

@KiptonBarros ve bence bunu groupByüyesini kullanarak yapabilirsiniz scala.collection.Iterable[A].
Louis-Jacob Lebel

20

scala.collection.immutable.Listartık bir .distinctyöntemi var.

Yani dirty.distinct, artık a Setveya biçimine dönüştürmeden arama yapmak mümkündür Seq.


1
.distinctiçin tanımlanmamıştır scala.collection.Iterable[A]. Bu durumda, bunun çalışması dirtyiçin a Seqveya Seta'ya yükseltme kullanmanız gerekir (yani .toList, .toSeqveya .toSetüyelerini kullanarak ).
Louis-Jacob Lebel

16

Kitpon'un çözümünü kullanmadan önce, a Setyerine a kullanmayı düşünün List, bu her bir öğenin benzersiz olmasını sağlar.

En liste işlemleri gibi ( foreach, map, filter, ...) setleri ve listeler için aynıdır, koleksiyon değişen çok kolay kodunda olabilir.


7

İlk etapta Set kullanmak, elbette bunu yapmanın doğru yoludur, ancak:

scala> List("a", "b", "a", "c").toSet.toList
res1: List[java.lang.String] = List(a, b, c)

İşler. Veya toSetdesteklediği gibiSıra Traversable arayüz.


1
Cevabınızı düzenledim çünkü Setuygular Traversable, değil Seq. Aradaki fark, Sequnsurlar için bir düzen garanti ederken Traversable, garanti etmemesidir.
Kipton Barros

0

Zaten Sıralanmış Listeler İçin

Sıklıkla ihtiyaç duyduğum gibi, bildiğiniz bir listenin farklı öğelerinin halihazırda sıralanmasını istiyorsanız , aşağıdakiler yaklaşık iki kat hız sağlar .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

0-99 arası 100.000.000 rastgele İntlik bir listede performans sonuçları:

distinct        : 0.6655373s
distinctOnSorted: 0.2848134s

MutableList veya ListBuffer ile Performans

Daha değişken / işlevsel olmayan bir programlama yaklaşımı, değişmez bir listenin başına geçmekten daha hızlı olabilir gibi görünse de, uygulama aksini gösterir. Değişmez uygulama sürekli olarak daha iyi performans gösterir. Tahminime göre scala derleyici optimizasyonlarını değişmez koleksiyonlara odaklıyor ve bunda iyi bir iş çıkarıyor. (Başkalarının daha iyi uygulamalar sunmasını memnuniyetle karşılıyorum.)

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

Uygulamalar:

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
  }
}

Ölçek:

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")
  }
}

Bu, yalnızca 100 benzersiz değer olduğu için oldukça etkilidir, ancak değişmez bir yapı kullanırken çok daha fazlası olsaydı, başınız derde girer. Daha da hızlı gitmek için, bunu değiştirilebilir bir yapı ile uygulayabilirsiniz.
Nick

@Nick Başlangıçta bu şekilde olacağını düşündüm, ancak yukarıdaki düzenlemelere bakın.
voxoid

Bunun için neden değişmezin daha iyi olacağını anlayamadığım için yukarıdakileri kendim denedim, ancak farklı değerlerin sayısını önemli ölçüde artırsanız bile öyle olmaya devam ediyor. Ayrıca, başa gitmenin daha verimli olduğu, ancak sonunda sonucu tersine çevirmeden bile daha yavaş olduğu birkaç değiştirilebilir yapıyı denedim.
Nick

0

Özyineleme ve kalıp eşleştirmeyi de kullanabilirsiniz:

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)
}


1
removeDuplicates(tail.filter(_ != head))
jwvh

-3

inArr.distinct foreach println _


bu istenen çıktıyı yazdırır, OP onu iade etmeyi istemiyor mu (muhtemelen bir Liste olarak)?
RobP

-5

Algoritmik yol ...

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 " "
}

1
Bir listesi var, dizesi yok. Bu sorunun cevabı değil.
Tim Gautier
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.