Scala collection one-to-one mapping? - scala

Pardon me if it's simple, but what is the most efficient way to do the following in scala:
Say I have two collections A and B with exactly same number of elements. For example,
A = {objectA1, objectA2, .... objectAN}
B = {objectB1, objectB2, .... objectBN}
I would like to get {{objectA1, objectB1}, {objectA2, objectB2}, ... {objectAN, objectBN}}. Note that these collections might be very large.

Some additions to #Tomasz answer: If collections are very large it is inefficient to use a zip b because it will create a complete intermediate collection. There is an alternative:
scala> (a,b).zipped
res15: scala.runtime.Tuple2Zipped[Int,Seq[Int],Char,Seq[Char]] = scala.runtime.Tuple2Zipped#71060c3e
scala> (a,b,b).zipped // works also for Tuple3
res16: scala.runtime.Tuple3Zipped[Int,Seq[Int],Char,Seq[Char],Char,Seq[Char]] = scala.runtime.Tuple3Zipped#30b688e1
Internally, Tuple2Zipped and Tuple3Zipped use iterators. This makes it more efficient when you want to transform the zippers.

Zip them:
A zip B
Example:
scala> val a = Seq(1, 2, 3, 4, 5)
a: Seq[Int] = List(1, 2, 3, 4, 5)
scala> val b = Seq('a', 'b', 'c', 'd', 'e')
b: Seq[Char] = List(a, b, c, d, e)
scala> a zip b
res5: Seq[(Int, Char)] = List((1,a), (2,b), (3,c), (4,d), (5,e))
If A and B are iterators, this will create an iterator of pairs as well.

Related

Does scala collection's flatten keep order?

I have looked at the documentation of flatten and the example seems to indicate the order of the elements in the result maintains the order of the input. Is there a documentation or source code we can reference to make sure that it is the case? Or, is the documentation "Converts this collection of traversable collections into a collection formed by the elements of these traversable collections" enough to confirm this?
Update: My original question was not clear enough. I wanted to ask about the collections that maintain order internally (like List) and we use the default implicit traversable in flatten(). prayagupd has answered this question.
If you read the flatten function of scala 2.12.x, you can see it sequentially adding given inputs to a new collection.
//a sequential view of the collection
private def sequential: TraversableOnce[A] = this.asInstanceOf[GenTraversableOnce[A]].seq
def flatten[B](implicit asTraversable: A => /*<:<!!!*/ GenTraversableOnce[B]): CC[B] = {
val b = genericBuilder[B]
for (xs <- sequential)
b ++= asTraversable(xs).seq
b.result()
}
you can verify with example as well,
scala> List(List("order1", "order2"), List("order10", "order11")).flatten
res1: List[String] = List(order1, order2, order10, order11)
The order remains same even if you provide your own traversable,
scala> val asTraversable: List[String] => List[String] = list => list.map(elem => s"mutated $elem")
asTraversable: List[String] => List[String] = $$Lambda$1271/1988351538#513bec8c
scala> List(List("order1", "order2"), List("order10", "order11")).flatten(asTraversable)
res2: List[String] = List(mutated order1, mutated order2, mutated order10, mutated order11)
NOTE: above only applies to underlying data-structure that maintain the order.
For example Set does not maintain order
scala> Set(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).seq
res3: scala.collection.immutable.Set[Int] = Set(5, 10, 1, 6, 9, 2, 7, 3, 8, 4)

Significance of val or var in immutable objects

Does val or var make difference in immutable objects like lists or tuple?
scala> val ab = List(1,2,3)
ab: List[Int] = List(1, 2, 3)
scala> var ab = List(1,2,3)
ab: List[Int] = List(1, 2, 3)
I am beginner in scala.
You may be confusing two different aspects of immutability. The local variable ab refers to some object in memory, in this case a List. When you declare ab as a val you are instructing the compiler that ab will always refer to the same object. Because List is immutable, its contents will never change, but a var referring to it might be reassigned to some other List.
scala> import scala.collection.mutable.MutableList
import scala.collection.mutable.MutableList
scala> val a = List(1,2,3,4)
a: List[Int] = List(1, 2, 3, 4)
scala> val b = MutableList(1,2,3,4)
b: scala.collection.mutable.MutableList[Int] = MutableList(1, 2, 3, 4)
scala> var c = List(1,2,3,4)
c: List[Int] = List(1, 2, 3, 4)
Here, a is a val containing an immutable data structure. a refers to List(1,2,3,4) and will always do so.
The val b refers to a MutableList. We can change the internal contents of the MutableList but we cannot assign b to a different MutableList.
scala> b += 5
res6: b.type = MutableList(1, 2, 3, 4, 5)
scala> b = MutableList(2,3,5,7)
<console>:12: error: reassignment to val
b = MutableList(2,3,5,7)
^
With var c we have a variable that refers to an immutable data structure List but that can be reassigned to a different List.
scala> c = c :+ 5
c: List[Int] = List(1, 2, 3, 4, 5)
Note that the :+ operator (unlike the += operator above) does not change the List referred to by c. Instead it create a copy of the List with the element 5 appended. Because c was declared a var we can then assign this new list to c.
It's not really relevant whether the object that ab points to is mutable. val means that you cannot in the future assign ab to another value, while var allows it.
Try repeating the assignment in each case and see what happens:
scala> val ab = List(1,2,3)
ab: List[Int] = List(1, 2, 3)
scala> ab = List(1,2,3)
reassignment to val; not found: value ab
scala> var ab = List(1,2,3)
ab: List[Int] = List(1, 2, 3)
scala> ab = List(1,2,3)
ab: List[Int] = List(1, 2, 3)
It's a question of style.
In Scala using a val is generally preferred to using a var as in (functional) programming immutability makes it easier to reason about a program.
So if you can get what you want without resorting to var it is the way to go.
A typical application of a var would be if you want to use an immutable data structure and update it benifitting of structural sharing.
var data = List.empty[String] // var is importan here
def addToData(s: String) : Unit = { data = s :: data }
The same could be achieved by using a mutable datastructure
import scala.collection.mutable.ArrayBuffer
val data = ArrayBuffer.empty[String]
data += "Hello" // this function is here from the beginning
For an in depth discussion look at https://stackoverflow.com/a/4440614/344116 .

Zip two Arrays, always 3 elements of the first array then 2 elements of the second

I've manually built a method that takes 2 arrays and combines them to 1 like this:
a0,a1,a2,b0,b1,a3,a4,a5,b2,b3,a6,...
So I always take 3 elements of the first array, then 2 of the second one.
As I said, I built that function manually.
Now I guess I could make this a one-liner instead with the help of zip. The problem is, that zip alone is not enough as zip builds tuples like (a0, b0).
Of course I can flatMap this, but still not what I want:
val zippedArray: List[Float] = data1.zip(data2).toList.flatMap(t => List(t._1, t._2))
That way I'd get a List(a0, b0, a1, b1,...), still not what I want.
(I'd then use toArray for the list... it's more convenient to work with a List right now)
I thought about using take and drop but they return new data-structures instead of modifying the old one, so not really what I want.
As you can imagine, I'm not really into functional programming (yet). I do use it and I see huge benefits, but some things are so different to what I'm used to.
Consider grouping array a by 3, and array b by 2, namely
val a = Array(1,2,3,4,5,6)
val b = Array(11,22,33,44)
val g = (a.grouped(3) zip b.grouped(2)).toArray
Array((Array(1, 2, 3),Array(11, 22)), (Array(4, 5, 6),Array(33, 44)))
Then
g.flatMap { case (x,y) => x ++ y }
Array(1, 2, 3, 11, 22, 4, 5, 6, 33, 44)
Very similar answer to #elm but I wanted to show that you can use more lazy approach (iterator) to avoid creating temp structures:
scala> val a = List(1,2,3,4,5,6)
a: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> val b = List(11,22,33,44)
b: List[Int] = List(11, 22, 33, 44)
scala> val groupped = a.sliding(3, 3) zip b.sliding(2, 2)
groupped: Iterator[(List[Int], List[Int])] = non-empty iterator
scala> val result = groupped.flatMap { case (a, b) => a ::: b }
result: Iterator[Int] = non-empty iterator
scala> result.toList
res0: List[Int] = List(1, 2, 3, 11, 22, 4, 5, 6, 33, 44)
Note that it stays an iterator all the way until we materialize it with toList

Index with Many Indices

Is there a quick scala idiom to have retrieve multiple elements of a a traversable using indices.
I am looking for something like
val L=1 to 4 toList
L(List(1,2)) //doesn't work
I have been using map so far, but wondering if there was a more "scala" way
List(1,2) map {L(_)}
Thanks in advance
Since a List is a Function you can write just
List(1,2) map L
Although, if you're going to be looking things up by index, you should probably use an IndexedSeq like Vector instead of a List.
You could add an implicit class that adds the functionality:
implicit class RichIndexedSeq[T](seq: IndexedSeq[T]) {
def apply(i0: Int, i1: Int, is: Int*): Seq[T] = (i0+:i1+:is) map seq
}
You can then use the sequence's apply method with one index or multiple indices:
scala> val data = Vector(1,2,3,4,5)
data: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3, 4, 5)
scala> data(0)
res0: Int = 1
scala> data(0,2,4)
res1: Seq[Int] = ArrayBuffer(1, 3, 5)
You can do it with a for comprehension but it's no clearer than the code you have using map.
scala> val indices = List(1,2)
indices: List[Int] = List(1, 2)
scala> for (index <- indices) yield L(index)
res0: List[Int] = List(2, 3)
I think the most readable would be to implement your own function takeIndices(indices: List[Int]) that takes a list of indices and returns the values of a given List at those indices. e.g.
L.takeIndices(List(1,2))
List[Int] = List(2,3)

Mutating a mutable collection using map?

If you have a mutable data structure like an Array, is it possible to use map operations or something similar to change its values?
Say I have val a = Array(5, 1, 3), what the best way of say, subtracting 1 from each value? The best I've come up with is
for(i <- 0 until a.size) { a(i) = a(i) - 1 }
I suppose way would be make the array a var rather than a val so I can say
a = a map (_-1)
edit: fairly easy to roll my own if there's nothing built-in, although I don't know how to generalise to other mutable collections
scala> implicit def ArrayToMutator[T](a: Array[T]) = new {
| def mutate(f: T => T) = a.indices.foreach {i => a(i) = f(a(i))}
| }
ArrayToMutator: [T](a: Array[T])java.lang.Object{def mutate(f: (T) => T): Unit}
scala> val a = Array(5, 1, 3)
a: Array[Int] = Array(5, 1, 3)
scala> a mutate (_-1)
scala> a
res16: Array[Int] = Array(4, 0, 2)
If you don't care about indices, you want the transform method.
scala> val a = Array(1,2,3)
a: Array[Int] = Array(1, 2, 3)
scala> a.transform(_+1)
res1: scala.collection.mutable.WrappedArray[Int] = WrappedArray(2, 3, 4)
scala> a
res2: Array[Int] = Array(2, 3, 4)
(It returns a copy of the wrapped array so that chaining transforms is more efficient, but the original array is modified as you can see.)
How about using foreach since you don't really want to return a modified collection, you want to perform a task for each element. For example:
a.indices.foreach{i => a(i) = a(i) - 1}
or
a.zipWithIndex.foreach{case (x,i) => a(i) = x - 1}