복붙노트

[SCALA] 스칼라 : 객체 목록에서 중복 제거

SCALA

스칼라 : 객체 목록에서 중복 제거

나는 모두 같은 클래스에서 인스턴스화 된 개체 목록 [오브젝트]의 목록을 가지고있다. 이 클래스는 고유 Object.property이어야 필드가 있습니다. 개체의 목록을 반복하고 동일한 속성을 가진 모든 객체 (하지만 첫 번째)를 제거하는 가장 깨끗한 방법은 무엇입니까?

해결법

  1. ==============================

    1.

    list.groupBy(_.property).map(_._2.head)
    

    설명 : GROUPBY 방법은, 그룹핑을위한 키 요소를 변환하는 기능을 수용한다. 객체 => elem.property (컴파일러, X $ (1) 같은 것을 고유 한 이름을 생성) : _.property은 ELEM 단지 속기이다. 그래서 지금 우리가지도의지도가 [속성, 목록은 [] 객체]. 맵 [K는 V]에 이동 [(K를, V)]으로 연장된다. 그래서 그것은 목록처럼 통과하지만, 요소 튜플하다 할 수있다. 이것은 자바의지도 #의 entrySet ()와 유사합니다. 지도 상에있어서의 각 요소를 반복하고 여기에 함수를 적용함으로써 새로운 집합을 생성한다. (성질,리스트 [개체) => elem._2.head이 경우 함수 ELEM 대한 속기 _._ 2.head이다. _2 번째 요소를 반환 튜플 단순한 방법이다. 두 번째 요소는 목록 [개체]이며, 머리는 첫번째 요소를 반환

    당신이 원하는 유형을로 결과를 얻으려면 :

    import collection.breakOut
    val l2: List[Object] = list.groupBy(_.property).map(_._2.head)(breakOut)
    

    간단히 설명하기 위해,지도는 실제로는 두 개의 인수, 함수 및 결과를 구성하는 데 사용되는 개체를 기대하고있다. 첫 번째 코드에서 암시 적으로 표시 범위 등의 미리 정의 된 값들의 목록에서 컴파일러에 의해 제공되기 때문에 사용자가 제 2 값을 참조하지 니펫. 그 결과는 일반적으로 컨테이너 매핑으로부터 얻어진다. 이것은 일반적으로 좋은 일이있다. 목록에지도 우리는 우리가 결과로 원하는 컨테이너를 표현하려면, 배열에지도 그러나이 경우 배열 등을 반환, 목록을 반환합니다. 브레이크 아웃 방법이 사용되는 곳이다. 이 빌더 만 원하는 결과 유형을 보면 (결과를 빌드 것)를 구성한다. 그것은 일반적인 방법이며 우리가 명시 적으로 목록 [오브젝트] 또는 순서 (개체 # 속성을 가정 유형의 속성이다)을 유지하기로 (L2)를 입력했기 때문에 컴파일러는 제네릭 형식을 유추 :

    list.foldRight((List[Object](), Set[Property]())) {
      case (o, cum@(objects, props)) => 
        if (props(o.property)) cum else (o :: objects, props + o.property))
    }._1
    

    foldRight 초기 결과를 허용하는 방법 및 요소를 수용하고 업데이트 된 결과를 돌려주는 함수이다. 이 방법은 각 요소에 함수를 적용하여 최종 결과를 반환에 따른 결과를 갱신하는 각 요소를 반복 할. 우리가 물체에 붙이는 때문에 우리는 (보다는 foldLeft으로 왼쪽에서 오른쪽으로) 오른쪽에서 왼쪽으로 이동 -이 O (1), 그러나 추기는 O (N). 또한, 여기에 좋은 스타일을 관찰 우리는 요소를 추출하는 패턴 일치를 사용하고 있습니다.

    이 경우, 초기 결과는 빈리스트 및 세트의 쌍 (튜플)이다. 목록은 우리가 관심있는 결과이며, 세트는 우리가 이미 발생한 어떤 특성을 추적하는 데 사용됩니다. . 각 반복에서 우리는 세트 소품이 이미 설정에서,이 방법은 (적용 데프되어 적용) (재산을 포함 스칼라, OBJ (X)가 obj.apply (X로 번역되어 있는지 확인 A : A) :. 부울 즉 ) 요소를 수용하고 있는지 아닌지 참 / false를 반환합니다. 속성이 (이미 발생) 존재하는 경우, 결과는 그대로 반환됩니다. 그렇지 않으면 결과는 (:: 객체 O) 객체를 포함하도록 업데이트되고 속성이 기록됩니다 (소품 + o.property)

    업데이트 : @andreypopp는 일반적인 방법을 원했다 :

    import scala.collection.IterableLike
    import scala.collection.generic.CanBuildFrom
    
    class RichCollection[A, Repr](xs: IterableLike[A, Repr]){
      def distinctBy[B, That](f: A => B)(implicit cbf: CanBuildFrom[Repr, A, That]) = {
        val builder = cbf(xs.repr)
        val i = xs.iterator
        var set = Set[B]()
        while (i.hasNext) {
          val o = i.next
          val b = f(o)
          if (!set(b)) {
            set += b
            builder += o
          }
        }
        builder.result
      }
    }
    
    implicit def toRich[A, Repr](xs: IterableLike[A, Repr]) = new RichCollection(xs)
    

    사용:

    scala> list.distinctBy(_.property)
    res7: List[Obj] = List(Obj(1), Obj(2), Obj(3))
    

    또한 우리는 빌더를 사용하는 것처럼이 꽤 효율적이다 있습니다. 당신이 정말로 큰 목록이있는 경우 정기적 세트와 벤치 마크 성능 대신 변경 가능한 HashSet의를 사용할 수 있습니다.

  2. ==============================

    2.다음 순서를 유지 조금 비열한하지만 빠른 솔루션입니다 :

    다음 순서를 유지 조금 비열한하지만 빠른 솔루션입니다 :

    list.filterNot{ var set = Set[Property]()
        obj => val b = set(obj.property); set += obj.property; b}
    

    이 내부적으로 VAR를 사용하지만, 나는 그것을 이해하고 foldLeft-솔루션을보다 쉽게 ​​읽을 생각합니다.

  3. ==============================

    3.스칼라 2.13부터 가장 콜렉션은 이제 특정 변형 함수를 적용한 후 중복 무시 시퀀스의 모든 요소를 ​​반환 distinctBy 방법이 제공된다 :

    스칼라 2.13부터 가장 콜렉션은 이제 특정 변형 함수를 적용한 후 중복 무시 시퀀스의 모든 요소를 ​​반환 distinctBy 방법이 제공된다 :

    list.distinctBy(_.property)
    

    예를 들어 :

    List(("a", 2), ("b", 2), ("a", 5)).distinctBy(_._1) // List((a,2), (b,2))
    List(("a", 2.7), ("b", 2.1), ("a", 5.4)).distinctBy(_._2.floor) // List((a,2.7), (a,5.4))
    
  4. ==============================

    4.또 하나 개의 솔루션

    또 하나 개의 솔루션

    @tailrec
    def collectUnique(l: List[Object], s: Set[Property], u: List[Object]): List[Object] = l match {
      case Nil => u.reverse
      case (h :: t) => 
        if (s(h.property)) collectUnique(t, s, u) else collectUnique(t, s + h.prop, h :: u)
    }
    
  5. ==============================

    5.순서를 유지하여 다음

    순서를 유지하여 다음

    def distinctBy[L, E](list: List[L])(f: L => E): List[L] =
      list.foldLeft((Vector.empty[L], Set.empty[E])) {
        case ((acc, set), item) =>
          val key = f(item)
          if (set.contains(key)) (acc, set)
          else (acc :+ item, set + key)
      }._1.toList
    
    distinctBy(list)(_.property)
    
  6. ==============================

    6.나는 그것이 하나의 중간 단계, GROUPBY와 함께 작동하도록하는 방법을 발견 :

    나는 그것이 하나의 중간 단계, GROUPBY와 함께 작동하도록하는 방법을 발견 :

    def distinctBy[T, P, From[X] <: TraversableLike[X, From[X]]](collection: From[T])(property: T => P): From[T] = {
      val uniqueValues: Set[T] = collection.groupBy(property).map(_._2.head)(breakOut)
      collection.filter(uniqueValues)
    }
    

    이처럼 사용

    scala> distinctBy(List(redVolvo, bluePrius, redLeon))(_.color)
    res0: List[Car] = List(redVolvo, bluePrius)
    

    IttayD 최초의 솔루션과 유사하지만 고유 한 값의 설정에 따라 원래의 콜렉션을 필터링합니다. GROUPBY 하나,지도 하나와 필터 하나를 내 기대가 맞다면,이 세 가지 순회 않습니다. 그것은 원래의 콜렉션의 순서를 유지하지만 반드시 각 속성에 대한 첫 번째 값을 고려하지 않습니다. 예를 들면, (bluePrius, redLeon) 대신에 목록을 반환 할 수 있었다.

    물론, IttayD의 솔루션은 하나의 통과를 않기 때문에 여전히 빠릅니다.

    내 솔루션은 컬렉션이 실제로 동일한 자동차가있는 경우, 모두 출력 목록에있을 것입니다 단점이있다. 이 필터를 제거하고, [T]에서 타입 직접 uniqueValues ​​반환하여 고정 될 수있다. 그러나, 그것은 CanBuildFrom 것 같아 [에서에서 맵 [P, [T], T [T는]] 존재하지 않는 ... 제안을 환영합니다!

  7. ==============================

    7.위의 좋은 답변을 많이합니다. 그러나 distinctBy는하지만 그리 명확하지 않다 장소에서, 스칼라 이미 사용 중입니다. 아마도 당신은 같이 사용할 수 있습니다

    위의 좋은 답변을 많이합니다. 그러나 distinctBy는하지만 그리 명확하지 않다 장소에서, 스칼라 이미 사용 중입니다. 아마도 당신은 같이 사용할 수 있습니다

    def distinctBy[A, B](xs: List[A])(f: A => B): List[A] =
      scala.reflect.internal.util.Collections.distinctBy(xs)(f)
    
  8. from https://stackoverflow.com/questions/3912753/scala-remove-duplicates-in-list-of-objects by cc-by-sa and MIT license