How to easily convert IndexedSeq[Array[Int]] to Seq[Seq[Int]] in Scala? - scala

I have a function that takes a list of lists of integer, specifically Seq[Seq[Int]]. Then I produce this data from reading a text file and using split, and that produces a list of Array. That is not recognized by Scala, who raises a match error. But either IndexedSeq or Array alone are OK with a Seq[Int] function, apparently only the nested collection is an issue. How can I convert implicitly IndexedSeq[Array[Int]] to Seq[Seq[Int]], or how else could I do this other than using toList as demonstrated below? Iterable[Iterable[Int]] seems to be fine, for instance, but I can't use this.
scala> def g(x:Seq[Int]) = x.sum
g: (x: Seq[Int])Int
scala> g("1 2 3".split(" ").map(_.toInt))
res6: Int = 6
scala> def f(x:Seq[Seq[Int]]) = x.map(_.sum).sum
f: (x: Seq[Seq[Int]])Int
scala> f(List("1 2 3", "3 4 5").map(_.split(" ").map(_.toInt)))
<console>:9: error: type mismatch;
found : List[Array[Int]]
required: Seq[Seq[Int]]
f(List("1 2 3", "3 4 5").map(_.split(" ").map(_.toInt)))
^
scala> f(List("1 2 3", "3 4 5").map(_.split(" ").map(_.toInt).toList))
res8: Int = 18

The problem is that Array does not implement SeqLike. Normally, implicit conversions to ArrayOps or WrappedArray defined in scala.predef allow to use array just like Seq. However, in your case array is 'hidden' from implicit conversions as a generic argument. One solution would be to hint compiler that you can apply an implicit conversion to the generic argument like this:
def f[C <% Seq[Int]](x:Seq[C]) = x.map(_.sum).sum
This is similar to Paul's response above. The problem is that view bounds are deprecated in Scala 2.11 and using deprecated language features is not a good idea. Luckily, view bounds can be rewritten as context bounds as follows:
def f[C](x:Seq[C])(implicit conv: C => Seq[Int]) = x.map(_.sum).sum
Now, this assumes that there is an implicit conversion from C to Seq[Int], which is indeed present in predef.

How about this:
implicit def _convert(b:List[Array[Int]]):Seq[Seq[Int]]=b.map(_.toList)

Redefine f to be a bit more flexible.
Since Traversable is a parent of List, Seq, Array, etc., f will be compatible with these containers if it based on Traversable. Traversable has sum, flatten, and map, and that is all that's needed.
What is tricky about this is that
def f(y:Traversable[Traversable[Int]]):Int = y.flatten.sum
is finicky and doesn't work on a y of type List[Array[Int]] although it will work on Array[List[Int]]
To make it less finicky, some type view bounds will work.
Initially, I replaced your sum of sums with a flatten/sum operation.
def f[Y<%Traversable[K],K<%Traversable[Int]](y:Y):Int=y.flatten.sum
I found this also seems to work but I did not test as much:
def f[Y <% Traversable[K], K <% Traversable[Int]](y:Y):Int=y.map(_.sum).sum
This <% syntax says Y is viewable as Traversable[K] for some type K that is viewable as a Traversable of Int.
Define some different containers, including the one you need:
scala> val myListOfArray = List(Array(1,2,3),Array(3,4,5))
val myListOfArray = List(Array(1,2,3),Array(3,4,5))
myListOfArray: List[Array[Int]] = List(Array(1, 2, 3), Array(3, 4, 5))
scala> val myArrayOfList = Array(List(1,2,3),List(3,4,5))
val myArrayOfList = Array(List(1,2,3),List(3,4,5))
myArrayOfList: Array[List[Int]] = Array(List(1, 2, 3), List(3, 4, 5))
scala> val myListOfList = List(List(1,2,3),List(3,4,5))
val myListOfList = List(List(1,2,3),List(3,4,5))
myListOfList: List[List[Int]] = List(List(1, 2, 3), List(3, 4, 5))
scala> val myListOfRange = List(1 to 3, 3 to 5)
val myListOfRange = List(1 to 3, 3 to 5)
myListOfRange: List[scala.collection.immutable.Range.Inclusive] = List(Range(1, 2, 3), Range(3, 4, 5))
Test:
scala> f(myListOfArray)
f(myListOfArray)
res24: Int = 18
scala> f(myArrayOfList)
f(myArrayOfList)
res25: Int = 18
scala> f(myListOfList)
f(myListOfList)
res26: Int = 18
scala> f(myListOfRange)
f(myListOfRange)
res28: Int = 18

Related

How to pass ordering to scala.util.Sorting.quickSort

I'm trying to pass reverse ordering to the scala.util.Sorting.quickSort with this code:
val a = Array(3, 5, 1, 2)
scala.util.Sorting.quickSort(a)(Ordering.Int.reverse)
It doesn't work saying that:
Error: Unit does not take parameters
However, the code like that works:
val a = Array(3, 5, 1, 2)
a.sorted(Ordering.Int.reverse)
I don't understand why the quickSort example doesn't work? The Ordering.Int.reverse produce Ordering[Int] and according to documentation, the quickSort accepts it implicitly.
I'm running with Scala 2.12.7.
Expanding on Yuval Itzchakov's comment, here is the source code for scala.util.Sorting.quickSort:
def quickSort(a: Array[Int]): Unit = java.util.Arrays.sort(a)
A few lines down, another overloaded quicksort is defined as:
def quickSort[K: Ordering](a: Array[K]): Unit = {...
You mean to use this latter one. But when you call quickSort, the compiler is picking the first one which does not accept an Ordering in its parameter list so it complains about extra parameters. You will need to specify type parameter if you are using Int, Double, or Float as explained here. To summarize:
val a : Array[Int] = Array(3, 5, 1, 2)
val b = Array(3L, 5L, 1L, 2L) //Longs do not have this problem!
scala.util.Sorting.quickSort[Int](a)(Ordering.Int.reverse)
scala.util.Sorting.quickSort(b)(Ordering.Long.reverse)
println(a.toList)
println(b.toList)
List(5, 3, 2, 1)
List(5, 3, 2, 1)
According to documentation the signature of method that uses Ordering is:
def quickSort[K](a: Array[K])(implicit arg0: math.Ordering[K]): Unit
Sort array a with quicksort, using the Ordering on its elements. This
algorithm sorts in place, so no additional memory is used aside from
what might be required to box individual elements during comparison.
try this:
scala> val a = Array(3, 5, 1, 2)
a: Array[Int] = Array(3, 5, 1, 2)
scala> scala.util.Sorting.quickSort(a)(Ordering.Int.reverse)
<console>:13: error: Unit does not take parameters
scala.util.Sorting.quickSort(a)(Ordering.Int.reverse)
^
scala> scala.util.Sorting.quickSort[Int](a)(Ordering[Int].reverse)
scala> a
res2: Array[Int] = Array(5, 3, 2, 1)

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 .

What is the structure that is only enclosed by parentheses in scala?

Here's the problem:
I intend to retrieve a (Int, Int) object from a function, but I don't know how to get the second element. I've tried the following commands so as to retrieve the second value, or convert it to a Seq or List, but with no luck.
scala> val s = (1,2)
s: (Int, Int) = (1,2)
scala> s(1)
<console>:9: error: (Int, Int) does not take parameters
s(1)
^
scala> val ss = List(s)
ss: List[(Int, Int)] = List((1,2))
scala> ss(0)
res10: (Int, Int) = (1,2)
Could anyone give me some idea? Thanks a lot!
val s = (1, 2)
is syntatic sugar and creates a Tuple2, or in other words is equivalent to new Tuple2(1, 2). You can access elements in tuples with
s._1 // => 1
s._2 // => 2
Likewise, (1, 2, 3) would create a Tuple3, which also has a method _3 to access the third element.

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)

Missing parameter type for expanded function in Scala ArrayBuffer

While trying to run the following snippet from Scala for the impatient:
val b = ArrayBuffer(1,7,2,9)
val bSorted = b.sorted(_ < _)
I get the following error:
error: missing parameter type for expanded function ((x$1, x$2) => x$1.$less(x$2))
val bSorted = b.sorted(_ < _)
Can somebody explain what might be going on here. Shouldn't the parameter type be inferred from the contents of the ArrayBuffer or do I need to specify it explicitly?
Thanks
.sorted takes an implicit parameter of type Ordering (similar to Java Comparator). For integers, the compiler will provide the correct instance for you:
scala> b.sorted
res0: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 7, 9)
If you want to pass a comparison function, use sortWith:
scala> b.sortWith( _ < _ )
res2: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 7, 9)
scala> b.sortWith( _ > _ )
res3: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(9, 7, 2, 1)
However, pay attention, although ArrayBuffer is mutable, both sort methods will return a copy which is sorted, but the original won't be touched.