I would like to filter collection, so distance between adjacent elements would be at least 5.
So List(1, 2, 3, 4, 5, 6, 7, 11, 20) will become List(1, 6, 11, 20).
Is it possible to achieve in one pass using filter? What would be scala-way?
How about this one-liner:
scala> l.foldLeft(Vector(l.head)) { (acc, item) => if (item - acc.last >= 5) acc :+ item else acc }
res7: scala.collection.immutable.Vector[Int] = Vector(1, 6, 11, 20)
Try with foldLeft():
val input = List(1, 2, 3, 4, 5, 6, 7, 11, 20)
input.tail.foldLeft(List(input.head))((out, cur) =>
if(cur - out.head >= 5) cur :: out else out
).reverse
If it's not obvious:
Algorithm starts with first element (probably you need some edge cases handled) in the output collection
It iterates over all remaining elements from the input. If the difference between this element (cur) and first element of input is greater than or equal than 5, prepend to input. Otherwise skip and proceed
input was built by prepending and examining head to get better performance. .reverse is needed in the end
This is basically how you would implement this in imperative way, but with more concise syntax.
Related
why the Set type works perfectly well with for loops? instead, why the while loop running through a Set can not index by position?
Short Answer:
There is no for loop in scala. There is only for comprehension which is just syntactic sugar for some specific method calls.
for (i <- Set(1, 2, 33)) println(i)
//is translated to
Set(1, 2, 33).foreach { i => println(i) }
val newSet = for (i <- Set(1, 2, 33)) yield i*2
//is translated to
val newSet = Set(1, 2, 33).map { i => i*2 }
// There is more of such translations. Read doc :)
While loop on the other hand is just normal loop known from other languages. It just loops as long a condition is satisfied, nothing fancy here.
It means when you write for loop in scala you are just using foreach or map methods on Set[T] instance.
Because the index of any particular element in a Set is a private implementation detail, unlike in an IndexedSeq.
Consider for example:
(1 to 19).map(n => (n * n) % 19)
Vector(1, 4, 9, 16, 6, 17, 11, 7, 5, 5, 7, 11, 17, 6, 16, 9, 4, 1, 0): IndexedSeq[Int]
Now convert that to a Set:
(1 to 19).map(n => (n * n) % 19).toSet
HashSet(0, 5, 1, 6, 9, 17, 7, 16, 11, 4): scala.collection.immutable.Set[Int]
Can you tell in which order the duplicate elements were removed?
Now let's remove the even integers:
(1 to 19).map(n => (n * n) % 19).toSet.filter(_ % 2 == 1)
HashSet(5, 1, 9, 17, 7, 11): scala.collection.immutable.Set[Int]
Here, filter probably went through one immutable Set in the order that it's stored in and copied the elements that fit the predicate to a new immutable Set in the same order.
But what if we use a mutable set instead?
import scala.collection.mutable.HashSet
var someSet: HashSet[Int] = new HashSet()
someSet ++= ((1 to 19).map(n => (n * n) % 19))
HashSet(0, 1, 4, 5, 6, 7, 9, 11, 16, 17): scala.collection.mutable.HashSet[Int]
someSet.filter(_ % 2 == 1)
HashSet(1, 17, 5, 7, 9, 11): scala.collection.mutable.HashSet[Int]
(Scastie snippet)
Here's what I think happened: since we're dealing with a mutable Set here, what filter does is that it goes through and removes the elements that don't fit the predicate. But instead of shifting all later elements to fill the hole (as would be the case in a Java ArrayList<>), it simply grabs the current last element and shifts it to the now vacant position.
So 0, which now happens to be at position 0, gets removed, 17 replaces it at position 0, and the size property is decremented by 1.
No, wait, that's not exactly what happened. You'd have to look at the scala.collection.mutable.HashSet source to figure out exactly what happened.
But it doesn't actually matter. The API does not indicate any obligation to reveal the order the elements are internally stored in. Our assumption that a REPL displays the elements in the order they're stored in might well turn out to be wrong altogether.
I have a list
what I would like to do is
def someRandomMethod(...): ... = {
val list = List(1, 2, 3, 4, 5)
if(list.isEmpty) return list
list.differentScanLeft(list.head)((a, b) => {
a * b
})
}
which returns
List(1, 2, 6, 12, 20) rather than List(1, 2, 6, 24, 120)
Is there such API?
Thank you,
Cosmir
scan is not really the right method for this, you want to use sliding to generate a list of adjacent pairs of values:
(1::list).sliding(2).map(l => l(0)*l(1))
More generally, it is sometimes necessary to pass data on to the next iteration when using scan. The standard solution to this is to use a tuple (state, ans) and then filter out the state with another map at the end.
You probably want to use zip
val list = List(1,2,3,4,5,6)
val result = list.zip(list.drop(1)).map{case (a,b) => a*b}
println(result)
> List(2, 6, 12, 20, 30)
For a practical exercise I need to define a function that basically changes the index of every value in an odd index in a list, so that I would get this:
changePairs(List(1,2,3,4,5,6,7,8,9,10,11))
//> res62: List[Int] = List(2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 11)
changePairs(List(2,2,30,4,50,6,7,80,9,100))
//> res63: List[Int] = List(2, 2, 4, 30, 6, 50, 80, 7, 100, 9)
So basically I need to swap the places of each odd-even pair, and in case I'm left with a single odd element at the last index (11 in the first example), I leave it as it is.
I have this but it's definitely not working, and I'm not really sure why.
def changePairs(a: List[Int]) = a.zipWithIndex.map {
case (s,i) => if (i % 2 != 0) a.patch(i,Seq(s),1); a.patch(i-2,Seq(s),0);
}
Here's one way:
def changePairs(a: List[Int]) = a.grouped(2).flatMap {
case List(a, b) => List(b, a)
case a => a
}.toList
changePairs(List(1, 2, 3, 4, 5, 6, 7)) // List(2, 1, 4, 3, 6, 5, 7)
Main idea that gets you going is once you think of grouping the list into sublists of two elements, which is what grouped(2) does. From then on it's an easy ride - describe two cases, one with two elements (in that case we flip them) and one with only one element, such as 7 in my example, in which case we just leave it be. We use flatMap to flatten the resulting list of 2-element lists into one big list, and we do .toList to get out of an iterator that we got from grouped.
EDIT:
I now saw a.grouped(2).map(_.reverse).flatten.toList in the comments. Yeah, that works too, it's the same as this but much less verbose since instead of "manually" swapping the elements, we just do a reverse on each sublist.
You could also use recursion and pattern matching. This is efficient as you are only going through the list once:
def changePairs(l: List[Int]): List[Int] = {
l match {
case a :: b :: tail => b :: a :: changePairs(tail)
case _ => Nil
}
}
For example I have following Scala list, I want get a sublist until there is a requirement can be met.
val list = Seq(1,2,3,4,5,5,4,1,2,5)
The requirement is the number is 5, so I want the result as:
Seq(1,2,3,4)
Currently I use Scala collection's indexWhere and splitAt to return:
list.splitAt(list.indexWhere(x => x == 5))
(Seq(1,2,3,4), Seq(5,5,4,1,2,5))
I am not sure there are more better ways to achieve the same with better Scala collection's method I didn't realise?
You can use takeWhile:
scala> val list = Seq(1,2,3,4,5,5,4,1,2,5)
list: Seq[Int] = List(1, 2, 3, 4, 5, 5, 4, 1, 2, 5)
scala> list.takeWhile(_ != 5)
res30: Seq[Int] = List(1, 2, 3, 4)
Use span like this,
val (l,r) = list.span(_ != 5)
which delivers
l: List(1, 2, 3, 4)
r: List(5, 5, 4, 1, 2, 5)
Alternatively, you can write
val l = list.span(_ != 5)._1
to access only the first element of the resulting tuple.
This bisects the list at the first element that does not hold the condition.
As I noted in the title, how to compare the element of index N with element of index N+1, if elements compared are exactly the same, yield element only once.
I know I can use toSet, to get a set of unique elements, but this does not help me because, my list can contain duplicated elements but duplicated element can't be the next element in my list.
val ll = List(1, 2, 3, 6, 3, 7, 5, 5, 6, 3)
// Desired output: List(1, 2, 3, 6, 3, 7, 5, 6, 3)
I got a "near working solution" using zipWithIndex.collect, but when I compare inside it, index runs OutOfBounds. I can make this to work if I can use two conditions inside, first check maximum index to be index = (list.size-1) then I can compare list(index) != list(index+1) then yield list(index)
What I have tried without success (because of OutOfBounds), is:
times.zipWithIndex.collect
{
case (element, index)
// index+1 will be incremented out of my list
if (times(index) != times(index+1)) => times(index)
}
This can work if I can use one more condition to limit index, but does not work with two conditions:
times.zipWithIndex.collect
{
case (element, index)
if (index < times.size)
if (times(index) != times(index+1)) => times(index)
}
I appreciate any kind of alternative.
how about
ll.foldLeft(List[Int]())((acc, x) => acc match {case Nil => List(x) case y => if (y.last == x) y else y :+ x})
Here's my alternative using the sliding function:
val ll = List(1, 2, 3, 6, 3, 7, 5, 5, 6, 3)
ll.sliding(2)
.filter( t => t.length > 1 && t(0) != t(1) )
.map( t => t(0) )
.toList :+ ll.last
You can use zip the list with itself, dropping the first element so that you compare elements at index N with N + 1. You only need to append the last element (you may want to use a ListBuffer as appending the last element requires to copy the list).
val r = times.zip(times.drop(1)).withFilter(t => t._1 != t._2).map(_._1) :+ times.last
scala> val times = List(1, 2, 3, 6, 3, 7, 5, 5, 6, 3)
times: List[Int] = List(1, 2, 3, 6, 3, 7, 5, 5, 6, 3)
scala> val r = times.zip(times.drop(1)).withFilter(t => t._1 != t._2).map(_._1) :+ times.last
r: List[Int] = List(1, 2, 3, 6, 3, 7, 5, 6, 3)