Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <pre><code>list.groupBy(_.property).map(_._2.head) </code></pre> <p>Explanation: The groupBy method accepts a function that converts an element to a key for grouping. <code>_.property</code> is just shorthand for <code>elem: Object =&gt; elem.property</code> (the compiler generates a unique name, something like <code>x$1</code>). So now we have a map <code>Map[Property, List[Object]]</code>. A <code>Map[K,V]</code> extends <code>Traversable[(K,V)]</code>. So it can be traversed like a list, but elements are a tuple. This is similar to Java's <code>Map#entrySet()</code>. The map method creates a new collection by iterating each element and applying a function to it. In this case the function is <code>_._2.head</code> which is shorthand for <code>elem: (Property, List[Object]) =&gt; elem._2.head</code>. <code>_2</code> is just a method of Tuple that returns the second element. The second element is List[Object] and <code>head</code> returns the first element</p> <p>To get the result to be a type you want:</p> <pre><code>import collection.breakOut val l2: List[Object] = list.groupBy(_.property).map(_._2.head)(breakOut) </code></pre> <p>To explain briefly, <code>map</code> actually expects two arguments, a function and an object that is used to construct the result. In the first code snippet you don't see the second value because it is marked as implicit and so provided by the compiler from a list of predefined values in scope. The result is usually obtained from the mapped container. This is usually a good thing. map on List will return List, map on Array will return Array etc. In this case however, we want to express the container we want as result. This is where the breakOut method is used. It constructs a builder (the thing that builds results) by only looking at the desired result type. It is a generic method and the compiler infers its generic types because we explicitly typed l2 to be <code>List[Object]</code> or, to preserve order (assuming <code>Object#property</code> is of type <code>Property</code>):</p> <pre><code>list.foldRight((List[Object](), Set[Property]())) { case (o, cum@(objects, props)) =&gt; if (props(o.property)) cum else (o :: objects, props + o.property)) }._1 </code></pre> <p><code>foldRight</code> is a method that accepts an initial result and a function that accepts an element and returns an updated result. The method iterates each element, updating the result according to applying the function to each element and returning the final result. We go from right to left (rather than left to right with <code>foldLeft</code>) because we are prepending to <code>objects</code> - this is O(1), but appending is O(N). Also observe the good styling here, we are using a pattern match to extract the elements.</p> <p>In this case, the initial result is a pair (tuple) of an empty list and a set. The list is the result we're interested in and the set is used to keep track of what properties we already encountered. In each iteration we check if the set <code>props</code> already contains the property (in Scala, <code>obj(x)</code> is translated to <code>obj.apply(x)</code>. In <code>Set</code>, the method <code>apply</code> is <code>def apply(a: A): Boolean</code>. That is, accepts an element and returns true / false if it exists or not). If the property exists (already encountered), the result is returned as-is. Otherwise the result is updated to contain the object (<code>o :: objects</code>) and the property is recorded (<code>props + o.property</code>)</p> <p>Update: @andreypopp wanted a generic method:</p> <pre><code>import scala.collection.IterableLike import scala.collection.generic.CanBuildFrom class RichCollection[A, Repr](xs: IterableLike[A, Repr]){ def distinctBy[B, That](f: A =&gt; 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) </code></pre> <p>to use: </p> <pre><code>scala&gt; list.distinctBy(_.property) res7: List[Obj] = List(Obj(1), Obj(2), Obj(3)) </code></pre> <p>Also note that this is pretty efficient as we are using a builder. If you have really large lists, you may want to use a mutable HashSet instead of a regular set and benchmark the performance.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload