I am trying to get set difference between two sets as follows:
val set1 = Set(1, 2, 3, 4, 5)
// gives: scala.collection.immutable.Set[Int]
val set2 = Set(0 until 10)
// gives: scala.collection.immutable.Set[scala.collection.immutable.Range]
However, the following gives error:
scala> set2.diff(set1)
<console>:14: error: type mismatch;
found : scala.collection.immutable.Set[Int]
required: scala.collection.GenSet[scala.collection.immutable.Range]
set2.diff(set1)
^
How to convert Set[Range] to Set[Int] in easiest way ?
You could do this:
(0 to 10).toSet
or to stick with your initial idea (but less clean):
Set(0 until 10).flatten
which returns:
scala.collection.immutable.Set[Int] = Set(0, 5, 1, 6, 9, 2, 7, 3, 8, 4)
If you worked with a list you could do this (it doesn't seem to work for a Set):
List.range(0, 10)
Similarly to the answer suggested above
(0 until 10).toSet
also works and returns the same.
Related
I'm working with collections and found this a bit weird:
val a = Seq(1, 2, 3, 4, 5, 6)
a.filterNot(Seq(1, 2, 3))
//=> expected output: List(4, 5, 6)
//=> actual: compiler error
<console>:13: error: type mismatch;
found : Int(3)
required: Boolean
a.filterNot(List(1, 2, 3))
But, this version works:
val a = Seq(1, 2, 3, 4, 5, 6)
a.filterNot(Set(1, 2, 3))
//=> expected output: List(4, 5, 6)
//=> actual: List(4, 5, 6)
I wonder why this happens. I read both Seq and Set definitions but found any clue.
An easily overlooked fact is that a Set[A] extends A => Boolean. In other words, a Set is a predicate (one that checks for membership). This is why you can pass a Set to filterNot.
The difference is the apply() method for Seq and Set collections. Look at the definition of filterNot():
def filterNot(p: (A) ⇒ Boolean): Seq[A]
It takes a function that, when fed an element, returns a Boolean. Set does that. Seq does not.
Set(2,3,6).apply(0) //res0: Boolean = false
Seq(2,3,6).apply(0) //res1: Int = 2
So when you invoke a.filterNot(Set(1, 2, 3)), the Set passed in to the filterNot is applied to each of the elements from the Seq, a, which will result in true or false, which is what the filterNot() requires.
Why sameElements returned true for sets? Sets do not put elements in any order. In following two examples, the first one returns true but the second one returns false.
scala> val xs1 = Set(3, 2, 1, 4, 5, 6, 7)
xs1: scala.collection.immutable.Set[Int] = Set(5, 1, 6, 2, 7, 3, 4)
scala> val ys1 = Set(7, 2, 1, 4, 5, 6, 3)
ys1: scala.collection.immutable.Set[Int] = Set(5, 1, 6, 2, 7, 3, 4)
scala> xs1 sameElements ys1
res7: Boolean = true
scala> val xt1 = Set(1, 2, 3)
xt1: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
scala> val yt1 = Set(3, 2, 1)
yt1: scala.collection.immutable.Set[Int] = Set(3, 2, 1)
scala> xt1 sameElements yt1
res8: Boolean = false
For scala.collection.immutable.Set.sameElements(Set) to return true, both sets need to have the same elements, in the same order.
The default Set implementations are not ordered, so the element ordering depends upon the storage algorithm and the order in which elements were added. In your first example, the ordering turned out to be the same purely by coincidence.
If you use a scala.collection.immutable.SortedSet instead, you should get what you expect.
EDIT: If you want to just check whether two sets contain the same elements, regardless of order, just use equals. To illustrate this, try the following:
scala> val xt1 = Set(1, 2, 3)
xt1: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
scala> val yt1 = Set(3, 2, 1)
yt1: scala.collection.immutable.Set[Int] = Set(3, 2, 1)
scala> xt1 sameElements yt1
res0: Boolean = false
scala> xt1 == yt1
res1: Boolean = true
So sameElements is useful if you need to determine whether the ordering is the same as well as the elements themselves; otherwise, just use good ole ==.
Working with Lists in Scala I would like a simple way to get all elements but the last element. Is there a complementary method for .last similar to .head/.tail complement?
I'd rather not dirty up code with something like:
val x: List[String] = List("abc", "def", "ghi")
val allButLast: List[String] = x.reverse.tail.reverse
// List(abc, def)
Thanks.
init selects all elements but the last one.
List API for init.
scala> List(1,2,3,4,5)
res0: List[Int] = List(1, 2, 3, 4, 5)
scala> res0.init
res1: List[Int] = List(1, 2, 3, 4)
The 4 related methods here are head, tail, init, and last.
head and last get the first and final member, whereas
tail and init exclude the first and final members.
scala> val list = (0 to 10).toList
list: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> list.head
res0: Int = 0
scala> list.tail
res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> list.init
res2: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> list.last
res3: Int = 10
You should also take care, because all 4 of them are unsafe on the empty list and will throw exceptions.
These methods are defined on GenTraversableLike, which List implements.
That's init.
link to Scaladoc: http://www.scala-lang.org/api/2.11.5/index.html#scala.collection.immutable.List#init:Repr
def init: List[A]
Selects all elements except the last.
Also, note that it's defined in GenTraversableLike, so pretty much any Scala collection has this method.
For dropping off any number of items from the end of a list consider dropRight,
val xs = (1 to 5).toList
xs.dropRight(1)
List(1, 2, 3, 4)
xs.dropRight(2)
List(1, 2, 3)
xs.dropRight(10)
List()
scala> List(1,2,3,4,5,6,7).takeWhile(i=>i<5)
res1: List[Int] = List(1, 2, 3, 4)
What if I also need to include 5 in the result?
Assuming that the function that you will be using is more complicated than the taking first 5 elements then,
You can do
scala> List(1,2,3,4,5,6,7)
res5: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
scala> res5.takeWhile(_<5) ++ res5.dropWhile(_<5).take(1)
res7: List[Int] = List(1, 2, 3, 4, 5)
Also
scala> res5.span(_<5)
res8: (List[Int], List[Int]) = (List(1, 2, 3, 4),List(5, 6, 7))
scala> res8._1 ++ res8._2.take(1)
res10: List[Int] = List(1, 2, 3, 4, 5)
Also
scala> res5.take(res5.segmentLength(_<5, 0) + 1)
res17: List[Int] = List(1, 2, 3, 4, 5)
scala> res5.take(res5.indexWhere(_>5))
res18: List[Int] = List(1, 2, 3, 4, 5)
Edit
If this is not a parallelized computation and you won't be using the parallel collections:
var last = myList.head
val rem = myList.takeWhile{ x=>
last = x
x < 5
}
last :: rem
anonymous function forming a closure around the solution you want.
Previous Answer
I'd settle for the far less complicated:
.takeWhile(_ <= 5)
wherein you're just using the less than or equal operator.
List(1,2,3,4,5,6,7,8).takeWhile(_ <= 5)
This is best optimal solution for it
This question already has answers here:
In Scala 2, type inference fails on Set made with .toSet?
(3 answers)
Closed 8 years ago.
Strange as it may seem, this does not work:
scala> (1 to 6).toSet map (_ / 2)
<console>:8: error: missing parameter type for expanded function ((x$1) => x$1.$div(2))
(1 to 6).toSet map (_ / 2)
^
However, using to[Set] instead of toSet does:
scala> (1 to 6).to[Set] map (_ / 2)
res0: scala.collection.immutable.Set[Int] = Set(2, 0, 3, 1)
Huh. o_O
Also consider that this works:
scala> val s = (1 to 6).toSet; s map (_ / 2)
s: scala.collection.immutable.Set[Int] = Set(5, 1, 6, 2, 3, 4)
res1: scala.collection.immutable.Set[Int] = Set(2, 0, 3, 1)
As Range.Inclusive is a first-order type, as suggested by #AlexIv, keep in mind that this also won't work with List[Int]:
scala> List(1, 2, 3, 4, 5, 6).toSet map (_ / 2)
<console>:8: error: missing parameter type for expanded function ((x$1) => x$1.$
div(2))
List(1, 2, 3, 4, 5, 6).toSet map (_ / 2)
^
And as previously, this works:
scala> val s = List[Int](1, 2, 3, 4, 5, 6).toSet; s map (_ / 2)
s: scala.collection.immutable.Set[Int] = Set(5, 1, 6, 2, 3, 4)
res3: scala.collection.immutable.Set[Int] = Set(2, 0, 3, 1)
Edit: duplicate of Type inference fails on Set made with .toSet?
The typer phase (scala -Xprint:typer) hides the answer:
private[this] val res7: <error> = Predef.intWrapper(1).to(6).toSet[B].map[B, That]({
((x: Nothing) => Predef.identity[Nothing](x))
})();
(1 to 6) returns a Range.Inclusive, which is a first-order type and not a type constructor, it's not parameterized, but Set[A] expects/requires you to provide it some type and returns you a type. When you call toSet, scalac expects some type, cause Inclusive doesn't have toSet method, it's inherited from TraversableOnce and is a generic method, so you need explicitly provide some type:
(1 to 6).toSet[Int].map(identity)
res0: scala.collection.immutable.Set[Int] = Set(5, 1, 6, 2, 3, 4)
toBuffer also doesn't work, other conversions works perfectly and this two methods have a similar implementation:
def toBuffer[B >: A]: mutable.Buffer[B] = to[ArrayBuffer].asInstanceOf[mutable.Buffer[B]]
def toSet[B >: A]: immutable.Set[B] = to[immutable.Set].asInstanceOf[immutable.Set[B]]