55

Possible Duplicate:
LINQ analogues in Scala

I am looking for chart which shows equivalents in Scala of LINQ methods for IEnumerable:

  • First is head
  • Select is map
  • SingleOrDefault is ... (I don't know)
  • ... and so on

Does anyone know anything of such "translate" table?

Community
  • 1
  • 1
greenoldman
  • 16,895
  • 26
  • 119
  • 185

2 Answers2

135

I am only listing out the equivalents of functions from Enumerable<A>. This is incomplete as of now. I will try to update this later with more.

xs.Aggregate(accumFunc)              -> xs.reduceLeft(accumFunc)
xs.Aggregate(seed, accumFunc)        -> xs.foldLeft(seed)(accumFunc)
xs.Aggregate(seed, accumFunc, trans) -> trans(xs.foldLeft(seed)(accumFunc))
xs.All(pred)                         -> xs.forall(pred)
xs.Any()                             -> xs.nonEmpty
xs.Any(pred)                         -> xs.exists(pred)
xs.AsEnumerable()                    -> xs.asTraversable // roughly
xs.Average()                         -> xs.sum / xs.length
xs.Average(trans)                    -> trans(xs.sum / xs.length)
xs.Cast<A>()                         -> xs.map(_.asInstanceOf[A])
xs.Concat(ys)                        -> xs ++ ys
xs.Contains(x)                       -> xs.contains(x) //////
xs.Contains(x, eq)                   -> xs.exists(eq(x, _))
xs.Count()                           -> xs.size
xs.Count(pred)                       -> xs.count(pred)
xs.DefaultIfEmpty()                  -> if(xs.isEmpty) List(0) else xs // Use `mzero` (from Scalaz) instead of 0 for more genericity
xs.DefaultIfEmpty(v)                 -> if(xs.isEmpty) List(v) else xs
xs.Distinct()                        -> xs.distinct
xs.ElementAt(i)                      -> xs(i)
xs.ElementAtOrDefault(i)             -> xs.lift(i).orZero // `orZero` is from Scalaz
xs.Except(ys)                        -> xs.diff(ys)
xs.First()                           -> xs.head
xs.First(pred)                       -> xs.find(pred) // returns an `Option`
xs.FirstOrDefault()                  -> xs.headOption.orZero
xs.FirstOrDefault(pred)              -> xs.find(pred).orZero
xs.GroupBy(f)                        -> xs.groupBy(f)
xs.GroupBy(f, g)                     -> xs.groupBy(f).mapValues(_.map(g))
xs.Intersect(ys)                     -> xs.intersect(ys)
xs.Last()                            -> xs.last
xs.Last(pred)                        -> xs.reverseIterator.find(pred) // returns an `Option`
xs.LastOrDefault()                   -> xs.lastOption.orZero
xs.LastOrDefault(pred)               -> xs.reverseIterator.find(pred).orZero
xs.Max()                             -> xs.max
xs.Max(f)                            -> xs.maxBy(f)
xs.Min()                             -> xs.min
xs.Min(f)                            -> xs.minBy(f)
xs.OfType<A>()                       -> xs.collect { case x: A => x }
xs.OrderBy(f)                        -> xs.sortBy(f)
xs.OrderBy(f, comp)                  -> xs.sortBy(f)(comp) // `comp` is an `Ordering`.
xs.OrderByDescending(f)              -> xs.sortBy(f)(implicitly[Ordering[A]].reverse)
xs.OrderByDescending(f, comp)        -> xs.sortBy(f)(comp.reverse)
Enumerable.Range(start, count)       -> start until start + count
Enumerable.Repeat(x, times)          -> Iterator.continually(x).take(times)
xs.Reverse()                         -> xs.reverse
xs.Select(trans)                     -> xs.map(trans) // For indexed overload, first `zipWithIndex` and then `map`.
xs.SelectMany(trans)                 -> xs.flatMap(trans)
xs.SequenceEqual(ys)                 -> xs.sameElements(ys)
xs.Skip(n)                           -> xs.drop(n)
xs.SkipWhile(pred)                   -> xs.dropWhile(pred)
xs.Sum()                             -> xs.sum
xs.Sum(f)                            -> xs.map(f).sum // or `xs.foldMap(f)`. Requires Scalaz.
xs.Take(n)                           -> xs.take(n)
xs.TakeWhile(pred)                   -> xs.takeWhile(pred)
xs.OrderBy(f).ThenBy(g)              -> xs.sortBy(x => (f(x), g(x))) // Or: xs.sortBy(f &&& g). `&&&` is from Scalaz.
xs.ToArray()                         -> xs.toArray // Use `xs.toIndexedSeq` for immutable indexed sequence.
xs.ToDictionary(f)                   -> xs.map(f.first).toMap // `first` is from Scalaz. When f = identity, you can just write `xs.toMap`.
xs.ToList()                          -> xs.toList // This returns an immutable list. Use `xs.toBuffer` if you want a mutable list.
xs.Union(ys)                         -> xs.union(ys)
xs.Where(pred)                       -> xs.filter(pred)
xs.Zip(ys, f)                        -> (xs, ys).zipped.map(f) // When f = identity, use `xs.zip(ys)`

There is no direct equivalent of some functions, but it's fairly easy to roll your own. Here are some such functions.

Single:

def single[A](xs: Traversable[A]): A = {
  if(xs.isEmpty) sys error "Empty sequence!"
  else if(xs.size > 1) sys error "More than one elements!"
  else xs.head
}

SingleOrDefault:

def singleOrDefault[A : Zero](xs: Traversable[A]): A = {
  if(xs.isEmpty) mzero
  else if(xs.size > 1) sys error "More than one elements!"
  else xs.head
}

Join:

def join[A, B, K, R](outer: Traversable[A], inner: Traversable[B])
    (outKey: A => K, inKey: B => K, f: (A, B) => R): Traversable[R] = {
  for(o <- outer; i <- inner; if outKey(o) == inKey(i)) yield f(o, i)
}

GroupJoin:

def groupJoin[A, B, K, R](outer: Traversable[A], inner: Traversable[B])
    (outKey: A => K, inKey: B => K, f: (A, Traversable[B]) => R): Traversable[R] = {
  for(o <- outer) yield {
    val zs = for(i <- inner; if outKey(o) == inKey(i)) yield i
    f(o, zs)
  }
}

Notes:

  1. In idiomatic Scala, total functions are generally preferred over partial functions. So, idiomatic implementation of single and singleOrDefault would produce a value of type Either[Exception, A] instead of A. For example, here is refined implementation of single that returns Either[Exception, A].

    def single[A](xs: Traversable[A]): Either[Exception, A] = {
      if(xs.isEmpty) Left(new RuntimeException("Empty sequence!"))
      else if(xs.size > 1) Left(new RuntimeException("More than one elements!"))
      else Right(xs.head)
    }
    
  2. Scalaz's Zero/mzero are not quite same as C#'s default value mechanism. For details, you can refer to this post I wrote on this topic some time back.

  3. You can use enrich-my-library pattern to achieve the same effect as C#'s extension methods. Refer to this and this for details.

Nolan
  • 1,060
  • 1
  • 11
  • 34
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • 1
    Thank you VERY much!!! When updating, if there is no 1:1 mapping, please just put it as "no 1:1" mapping, thank you in advance. – greenoldman Nov 12 '11 at 20:30
  • The third `Aggregate` is incorrect. `trans(xs.foldLeft(seed)(accumFunc))` is proper. – Daniel C. Sobral Nov 12 '11 at 21:49
  • @missingfaktor: Would it be possible to use that list for docs.scala-lang.org? – soc Nov 12 '11 at 23:26
  • `start to start + count - 1` => `start until start + count` – soc Nov 12 '11 at 23:35
  • @soc: That's a great idea. There are a few functions (`Single*`, `GroupJoin`, `Join` etc.) which are still to be covered. Once that's done, you can use this for docs.scala-lang.org. – missingfaktor Nov 13 '11 at 05:06
  • @soc: Looks much better with `until`. Updated, thanks! – missingfaktor Nov 13 '11 at 05:06
  • @macias: What do you mean by that? Almost none of this is a 1:1 equivalent. – missingfaktor Nov 13 '11 at 05:10
  • @missingfaktor, I consider a translation which rather requires something more substantial than 1-liner as not as 1:1 mapping. – greenoldman Nov 13 '11 at 09:03
  • @soc: I updated my answer. You can now use it for docs.scala-lang.org. :-) – missingfaktor Nov 13 '11 at 11:36
  • @macias: Check the updated answer. The functions with no 1:1 mapping (as per your definition) are described separately. – missingfaktor Nov 13 '11 at 11:37
  • @missingfaktor, how can I upvote you by +100? ;-) Now, I am new to Scala but I think second checking for size in SingleXX would be faster with coll.tail.isEmpty -- this should be O(1), instead of size's O(n). – greenoldman Nov 14 '11 at 18:05
  • @macias: Oh, indeed. Most of the times I use `Vector` instead of `List`. Hence the oversight. I'll update the codes as per your suggestion. – missingfaktor Nov 14 '11 at 19:53
  • @macias: I wrote these codes with general collection interfaces (such as `Traversable`, `Seq`) in mind. The fact that `size` is an `O(n)` operation is specific to lists, and is not necessarily applicable to other types of collections. I am now updating the code accordingly. – missingfaktor Nov 14 '11 at 19:59
  • 7
    This is incredibly helpful. They must a create an ISO standard for collection APIs. – Dmitry Ornatsky Feb 08 '12 at 11:59
  • @DmitryOrnatsky, thanks. :) We don't have to go to those lengths. It will be more than sufficient if Microsoft's APIs conform to the common names and conventions, instead of inventing new ones. – missingfaktor Feb 08 '12 at 12:01
  • Your chart is inaccurate for `Average`: `xs.Average(trans)` is wrong, should be `trans(xs).sum / xs.length` for non-nullable types. For `xs.Average()`: average of ints is a double, not an int. As well, in both cases for nullable types nulls are not counted. – Vlad Oct 20 '13 at 20:25
  • As well, LINQ's `xs.Max(f)` is indeed `f(xs).max`. – Vlad Oct 20 '13 at 20:32
  • 2
    Excellent! I would make an up vote for this answer once a day! – python_kaa Jan 15 '15 at 10:52
  • How about `partition`?: `def partition(p: (A) ⇒ Boolean): (List[A], List[A])` Partitions this traversable collection in two traversable collections according to a predicate – yǝsʞǝla Feb 06 '15 at 03:29
0

I don't know anything about C# or LINQ, but is this what you're looking for?

scala> val l = List(1, 2, 3, 4, 5)
l: List[Int] = List(1, 2, 3, 4, 5)

scala> l.head
res0: Int = 1

scala> l.headOption
res1: Option[Int] = Some(1)

scala> l.map(_.toString)
res2: List[java.lang.String] = List(1, 2, 3, 4, 5)

scala> l(1)
res3: Int = 2

There's no method to get an element or a default, but this will work:

scala> scala.util.control.Exception.allCatch.opt(l(5)) getOrElse 0
res4: Int = 0
pr1001
  • 21,727
  • 17
  • 79
  • 125