How map function does sorting in scala - scala

The following code snippet
val keys=List(3,2,1,0)
val unsorted=List(1, 2, 3, 4)
val sorted =keys map unsorted
does sorting based on the keys.
Normally, map method takes a lambda and apply it to every element in the source. Instead the above code takes a list and does a sorting based on the index.
What is happening in this particular situation?

List is a partial function that goes from its indices to its values. You could look at it like this:
scala> List(3,2,1,0) map (i => List(1,2,3,4)(i))
res0: List[Int] = List(4, 3, 2, 1)
to verify
scala> val f: Int => Int = List(1, 2, 3, 4)
f: Int => Int = List(1, 2, 3, 4)

It does not sorting them, map takes a function, lets see:
that statement translates to:
keys.map(unsorted.apply)
Scala's List.apply(n: Int) function return nth element of list, in this case map makes it:
unsorted(3), unsorted(2), unsorted(1), unsorted(4)
which is
4,3,2,1 which is sorted only by chance in this case.
Test this:
scala> val keys = List(3,0,1,2)
keys: List[Int] = List(3, 0, 1, 2)
scala> val unsorted = List(10,20,30,40)
unsorted: List[Int] = List(10, 20, 30, 40)
scala> keys map unsorted
res0: List[Int] = List(40, 10, 20, 30)

In keys map unsorted for each value in keys we call the apply method in list unsorted which yields the element at a key position.
Swap elements in keys and note the outcome, for instance note the last key,
val keys=List(2,1,0,3)
val sorted = keys map unsorted
sorted: List[Int] = List(3, 2, 1, 4)

The function .map has not specific relation with sorting.
Your snipet is a specific case that happens to sort. If desugaring, it does as follows.
/* keys */List(3,2,1,0).map { i: Int =>
unsorted.apply(i)
}
It applies unsorted.apply with each value of keys (used as indexes).

It's not sorting at all. It is normal situation: map method takes a lambda and apply it to every element in the source. Just change your sorted list and you'll see it.

Related

Using `Seq::fill` with an existing sequence

I would want to fill a Seq[Seq[Int]] instance with an existing Seq[Int] by using the method fill(n1, n2)(element : A) of the Object Seq, as detailed in the documentation : http://www.scala-lang.org/api/2.12.3/scala/collection/Seq$.html#fillA(elem:=>A):CC[CC[A]]
So I wrote this call :
Seq.fill(width, width)(_random_values.) , with _random_values the existing Seq[Int].
The problem is that the parameter of the second list of fill is an element, not a Seq. So, what could I type to iterate on each _random_values's integers AND to execute the Seq.fill for each current integer ?
Seq.fill is more appropriate to create a Seq base on a static value. For your use case Seq.grouped might be a better fit:
val values: Seq[Int] = List(1, 2, 3, 4)
val result: Seq[Seq[Int]] = values.grouped(2).toSeq
result.foreach(println)
/*
List(1, 2)
List(3, 4)
*/

Scala get next index in the list from current item

Say for example I am mapping one list to the next, and in the map I want to do some calculation with the current item in the list with the next item in the list.
def someFunc(L: List[Integer]) : List[Integer] = {
L.collect {
case k if (k != L(L.length-1)) //do something with k and the next element
}
}
A simple example is I want to go through this List of Integers, and map each number onto the next number in the list divided by it.
E.g. (1,2,3) -> (2/1, 3/2) == (2, 1.5)
I had thought about doing this using indexOf but I don't think that is efficient having to search the whole list for the current element even though I am already traversing each element in the list anyway.
Use .sliding for this:
scala> val l = List(1, 2, 3)
l: List[Int] = List(1, 2, 3)
scala> l.sliding(2).toList
res0: List[List[Int]] = List(List(1, 2), List(2, 3))
scala> l.sliding(2).collect { case x::y::Nil if y != 0 => x / y.toDouble }.toList
res1: List[Double] = List(0.5, 0.6666666666666666)

Compare two list and get the index of same elements

val a = List(1,1,1,0,0,2)
val b = List(1,0,3,2)
I want to get the List of indices of elements of "List b" which are existing in "List a".
Here output to be List(0,1,3)
I tried this
for(x <- a.filter(b.contains(_))) yield a.indexOf(x))
Sorry. I missed this. The list size may vary. Edited the Lists
Is there a better way to do this?
If you want a result of indices, it's often useful to start with indices.
b.indices.filter(a contains b(_))
REPL tested.
scala> val a = List(1,1,1,0,0,2)
a: List[Int] = List(1, 1, 1, 0, 0, 2)
scala> val b = List(1,0,3,2)
b: List[Int] = List(1, 0, 3, 2)
scala> b.indices.filter(a contains b(_))
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 3)
val result = (a zip b).zipWithIndex.flatMap {
case ((aItem, bItem), index) => if(aItem == bItem) Option(index) else None
}
a zip b will return all elements from a that have a matching pair in b.
For example, if a is longer, like in your example, the result would be List((1,1),(1,0),(1,3),(0,2)) (the list will be b.length long).
Then you need the index also, that's zipWithIndex.
Since you only want the indexes, you return an Option[Int] and flatten it.
You can use indexed for for this:
for{ i <- 0 to b.length-1
if (a contains b(i))
} yield i
scala> for(x <- b.indices.filter(a contains b(_))) yield x;
res27: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 3)
Here is another option:
scala> val a = List(1,1,1,0,0,2)
a: List[Int] = List(1, 1, 1, 0, 0, 2)
scala> val b = List(1,0,3,2)
b: List[Int] = List(1, 0, 3, 2)
scala> b.zipWithIndex.filter(x => a.contains(x._1)).map(x => x._2)
res7: List[Int] = List(0, 1, 3)
I also want to point out that your original idea of: Finding elements in b that are in a and then getting indices of those elements would not work, unless all elements in b contained in a are unique, indexOf returns index of the first element. Just heads up.

flatten and flatMap in scala

I'll like to check if I have understood flatten and flatMap functions correctly.
1) Am I correct that flatten works only when a collection constitutes of other collections. Eg flatten would work on following lists
//list of lists
val l1 = List(List(1,1,2,-1,3,1,-4,5), List("a","b"))
//list of a set, list and map
val l2 = List(Set(1,2,3), List(4,5,6), Map('a'->"x",'b'->"y"))
But flatten will not work on following
val l3 = List(1,2,3)
val l4 = List(1,2,3,List('a','b'))
val s1 = "hello world"
val m1 = Map('h'->"h", 'e'->"e", 'l'->"l",'o'->"0")
'flatten' method would create a new list consisting of all elements by removing the hierarchy. Thus it sort of 'flattens' the collection and brings all elements at the same level.
l1.flatten
res0: List[Any] = List(1, 1, 2, -1, 3, 1, -4, 5, a, b)
l2.flatten
res1: List[Any] = List(1, 2, 3, 1, 5, 6, (a,x), (b,y))
2) 'flatMap' first applies a method to elements of a list and then flattens the list. As we noticed above, the flatten method works if lists have a hierarchy (contain other collections). Thus it is important that the method we apply to the elements returns a collection otherwise flatMap will not work
//we have list of lists
val l1 = List(List(1,1,2,-1,3,1,-4,5), List("a","b"))
l1 flatMap(x=>x.toSet)
res2: List[Any] = List(5, 1, -4, 2, 3, -1, a, b)
val l2 = List(Set(1,2,3), List(1,5,6), Map('a'->"x",'b'->"y"))
l2.flatMap(x=>x.toSet)
res3: List[Any] = List(1, 2, 3, 1, 5, 6, (a,x), (b,y))
val s1 = "hello world"
s1.flatMap(x=>Map(x->x.toString))
We notice above that s1.flatten didn't work but s1.flatMap did. This is because, in s1.flatMap, we convert elements of a String (characters) into a Map which is a collection. Thus the string got converted into a collection of Maps like (Map('h'->"h"), Map('e'->"e"), Map('l'->"l"),Map ('l'->"l"),Map('o'->"o")....) Thus flatten will work now. Note that the Map created is not Map('h'->"h", 'e'->"e", 'l'->"l",....).
Take a look at the full signature for flatten:
def flatten[B](implicit asTraversable: (A) ⇒ GenTraversableOnce[B]): List[B]
As you can see, flatten takes an implicit parameter. That parameter provides the rules for how to flatten the given collection types. If the compiler can't find an implicit in scope then it can be provided explicitly.
flatten can flatten almost anything as long as you provide the rules to do so.
Flatmap is basically a map operation followed by a flatten

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)