Transpose unevenly-sized lists - scala

I'm having some trouble transposing lists with uneven amounts of elements. Let's say our data is:
ABC
E
GHI
I want my list to be:
List(List('A','E','G'), List('B',' ','H'), List('C',' ','I'))
I cannot manage to get empty spaces (' ') where I need them. With my code:
val l = List("ABC", "E", "GHI")
println(((l map (_.toArray)) toArray).transpose map (_.toList) toList)
// I get: List(List('A', 'E', 'G'), List('B', 'H'), List('C', 'I'))
A solution may be to get the longest line and add white spaces to the rest of the lines, but it's really not clean. Is there a simple solution to this?

Here is a code-golf solution for an input list l:
(0 until l.map(_.size).max).map(i => l.map(s => Try(s(i)).getOrElse(' ')))
which returns:
Vector(List(A, E, G), List(B, , H), List(C, , I))
This:
Retrieves the maximum length of a string in the input list.
Loop from 0 until the max length of a string in the input list
Within each loop it gets each element's char at the given index.
The Try is used to handle items whose length is shorter than the current index being handled. And these cases return " " instead.
To use Try, you need to import:
import scala.util.Try

One approach is to use padTo, although this will involve multiple traversals of the list:
val l = List("ABC", "E", "GHI")
val maxSize = l.map(_.size).max // 3
val transposed = l.map(_.toList).map(_.padTo(maxSize, ' ')).transpose
// List[List[Char]] = List(List(A, E, G), List(B, , H), List(C, , I))

Related

how to iterate over array[string] in spark scala?

enter image description herehere is my sample input:
val list=List("a;bc:de;f","uvw:xy;z","123:456")
I am applying following operation
val upper=list.map(x=>x.split(":")).map(x=>x.split(";"))
but it is throwing error-
error: value split is not a member of Array[String]
can anyone help how to use both split so that i can get answer!
Thank you in advance.
Using list.map(x=>x.split(":")) will give you a list of Array.
upper: List[Array[String]] = List(Array(a;bc, de;f), Array(uvw, xy;z), Array(123, 456))
Mapping afterwards, you can see that the item will be an array where you are trying to run split on.
You might useflatMap instead which will first give you List(a;bc, de;f, uvw, xy;z, 123, 456) and then you can use map on those items splitting on ;
val upper = list.flatMap(_.split(":")).map(_.split(";"))
Output
upper: List[Array[String]] = List(Array(a, bc), Array(de, f), Array(uvw), Array(xy, z), Array(123), Array(456))
You can use split with multiple delimiters in one map iteration :
val upper = list.map(x => x.split("[:;]"))
//upper: List[Array[String]] = List(Array(a, bc, de, f), Array(uvw, xy, z), Array(123, 456))
Here is the code i have tried and it worked:
val upper=list.map(x=>x.split(":")).map(x=>x.map(x=>x.split(";")))
which gives the output:
upper: List[Array[Array[String]]] = List(Array(Array(a, bc), Array(de, f)), Array(Array(uvw), Array(xy, z)), Array(Array(123), Array(456)))

How to create a new list inside foreach in scala

I am newbie to scala and just trying out stuff, below is what I am trying
scala> var col = List[String]()
col: List[String] = List()
scala> List("a", "b", "c").foreach(x => x :: col)
scala> println(col)
List()
Actually, I was expecting col to contain a,b,c, what am I missing?
You need an assignment in the foreach
scala> var col = List[String]()
col: List[String] = List()
scala> List("a", "b", "c").foreach(x => {col = x :: col})
scala> col
res0: List[String] = List(c, b, a)
The operation x :: col simply returns a new list consisting of the element x prepended to col, the original col is not changed. You would need to reassign col to this newly generated list.
Note however that this would not typically be considered idiomatic Scala since you are using side-effects.
The :: method on list does not add anything to the list, it creates a new list with the value prepended to it, you are discarding this new list instead of reassigning it to col. x => col = x :: col will add each element of your list to col. Note that col will then be List("c","b","a"), the order is reversed because you are pre-pending the elements to col.
Note that foreach returns nothing and is designed for side-effecting operations. If you simply want to transform a collection or load elements into another collection there are better methods to use.
For your specific operation, the most appropriate method is foldRight which iterates elements in reverse order, right-to-left. We want to iterate in reverse here because when you prepend elements to a list one at a time the order gets reversed.
val col = List("a", "b", "c").foldRight(List[String]())((x, acc) => x :: acc) will produce a List("a", "b", "c"). This has the advantage that we no longer need to use var to declare a mutable variable, and in fact we don'
to need to declare our list ahead of time at all.
note, we could have used some special syntax to save some typing
val col = List("a", "b", "c").foldRight(List[String]())(_ :: _)
The underscores give us a shorter syntax to write function literals, I'll leave up to you to decide if it's more clear or not.

Scala - List of Strings to Square Cypher String

I am following an exercise in Scala to build a square cypher. Here's an overview of the problem:
List("hello", "world", "fille", "r") is written taking the first letter from each String in the List and concatenating to the final string. Essentially, if you write them in square cypher form, you get:
hwfr
eoi
lrl
lll
ode
Which if you read from top to bottom, left to right, is the message. My expected output needs to be a List[String] that becomes List("hwfr", "eoi", ...). I don't know what methods or where to start in order to manipulate the original List in order to adhere to the form that I need. I can't map zip since zip only takes two arguments and I have an indeterminate amount of Strings. I'm not exactly sure how I might iterate over this List to get the result I need and would appreciate any suggestions or tips.
scala> val list = List("hello", "world", "fille", "rtext")
list: List[String] = List(hello, world, fille, rtext)
scala> list.transpose
res6: List[List[Char]] = List(List(h, w, f, r), List(e, o, i, t), List(l, r, l, e), List(l, l, l, x), List(o, d, e, t))
does the trick, api
Here is a version which does not care about equal word length. There should be more efficient versions, but I wanted to keep it relatively short.
Basic idea: Find out how long the longest word is (max). Since you know that, you start with index i = 0 and take the character at that position i from each string and form a string from it until you are at i = max - 1 (which is the position of the last character of the longest word. When the words are not at equal length, you have to make sure that you don't access a character which is not there.
Example: i = 1, then you get e from hello, o from world, i from fille, but accessing character 1 on r would result in an exception. That is why we check for size of the string beforehand and in that case append the empty string. if(i < elem.size) elem(i) else ""
val list = List("hello", "world", "fille", "r")
val max = list.maxBy(_.size).size //gives you the size of the longest word
val result: List[String] = (0 until max).map(i => list.foldLeft("")
((s, elem) => s + (if(i < elem.size) elem(i) else "")))(collection.breakOut)
println(result) //List(hwfr, eoi, lrl, lll, ode)
Edit:
If you still want it to be readable from left-right/top-bottom (if they are not ordered by length and you don't want to order them), you can introduce spaces. Change if(i < elem.size) elem(i) else "" to if(i < elem.size) elem(i) else " ".
List("hello", "world", "fille", "r") would become List(hwfr, eoi , lrl , lll , ode ) and List("hello", "world", "r", "fille") would become List(hwrf, eo i, lr l, ll l, od e)

Map versus FlatMap on String

Listening to the Collections lecture from Functional Programming Principles in Scala, I saw this example:
scala> val s = "Hello World"
scala> s.flatMap(c => ("." + c)) // prepend each element with a period
res5: String = .H.e.l.l.o. .W.o.r.l.d
Then, I was curious why Mr. Odersky didn't use a map here. But, when I tried map, I got a different result than I expected.
scala> s.map(c => ("." + c))
res8: scala.collection.immutable.IndexedSeq[String] = Vector(.H, .e, .l, .l, .o,
". ", .W, .o, .r, .l,
I expected that above call to return a String, since I'm map-ing, i.e. applying a function to each item in the "sequence," and then returning a new "sequence."
However, I could perform a map rather than flatmap for a List[String]:
scala> val sList = s.toList
sList: List[Char] = List(H, e, l, l, o, , W, o, r, l, d)
scala> sList.map(c => "." + c)
res9: List[String] = List(.H, .e, .l, .l, .o, ". ", .W, .o, .r, .l, .d)
Why was a IndexedSeq[String] the return type of calling map on the String?
The reason for this behavior is that, in order to apply "map" to a String, Scala treats the string as a sequence of chars (IndexedSeq[String]). This is what you get as a result of the map invocation, where for each element of said sequence, the operation is applied. Since Scala treated the string as a sequence to apply map, that is what mapreturns.
flatMap then simply invokes flatten on that sequence afterwards, which then "converts" it back to a String
You also have an interesting "collection of Scala flatMap examples", the first of which illustrates that difference between flatMap and map:
scala> val fruits = Seq("apple", "banana", "orange")
fruits: Seq[java.lang.String] = List(apple, banana, orange)
scala> fruits.map(_.toUpperCase)
res0: Seq[java.lang.String] = List(APPLE, BANANA, ORANGE)
scala> fruits.flatMap(_.toUpperCase)
res1: Seq[Char] = List(A, P, P, L, E, B, A, N, A, N, A, O, R, A, N, G, E)
Quite a difference, right?
Because flatMap treats a String as a sequence of Char, it flattens the resulting list of strings into a sequence of characters (Seq[Char]).
flatMap is a combination of map and flatten, so it first runs map on the sequence, then runs flatten, giving the result shown.
You can see this by running map and then flatten yourself:
scala> val mapResult = fruits.map(_.toUpperCase)
mapResult: Seq[String] = List(APPLE, BANANA, ORANGE)
scala> val flattenResult = mapResult.flatten
flattenResult: Seq[Char] = List(A, P, P, L, E, B, A, N, A, N, A, O, R, A, N, G, E)
Your map function c => ("." + c) takes a char and returns a String. It's like taking a List and returning a List of Lists. flatMap flattens that back.
If you would return a char instead of a String you wouldn't need the result flattened, e.g. "abc".map(c => (c + 1).toChar) returns "bcd".
With map you are taking a list of characters and turning it into a list of strings. That's the result you see. A map never changes the length of a list – the list of strings has as many elements as the original string has characters.
With flatMap you are taking a list of characters and turning it into a list of strings and then you mush those strings together into a single string again. flatMap is useful when you want to turn one element in a list into multiple elements, without creating a list of lists. (This of course also means that the resulting list can have any length, including 0 – this is not possible with map unless you start out with the empty list.)
Use flatMap in situations where you run map followed by flattern. The specific situation is this:
• You’re using map (or a for/yield expression) to create a new collection from an existing collection.
• The resulting collection is a List of Lists.
• You call flatten immediately after map (or a for/yield expression).
When you’re in this situation, you can use flatMap instead.
Example: Add all the Integers from the bag
val bag = List("1", "2", "three", "4", "one hundred seventy five")
def toInt(in: String): Option[Int] = {
try {
Some(Integer.parseInt(in.trim))
} catch {
case e: Exception => None
}
}
Using a flatMap method
> bag.flatMap(toInt).sum
Using map method (3 steps needed)
bag.map(toInt) // List[Option[Int]] = List(Some(1), Some(2), None, Some(4), None)
bag.map(toInt).flatten //List[Int] = List(1, 2, 4)
bag.map(toInt).flatten.sum //Int = 7

Standard function to enumerate all strings of given length over given alphabet

Suppose, I have an alphabet of N symbols and want to enumerate all different strings of length M over this alphabet. Does Scala provide any standard library function for that?
Taking inspiration from another answer:
val letters = Seq("a", "b", "c")
val n = 3
Iterable.fill(n)(letters) reduceLeft { (a, b) =>
for(a<-a;b<-b) yield a+b
}
Seq[java.lang.String] = List(aaa, aab, aac, aba, abb, abc, aca, acb, acc, baa, bab, bac, bba, bbb, bbc, bca, bcb, bcc, caa, cab, cac, cba, cbb, cbc, cca, ccb, ccc)
To work with something other than strings:
val letters = Seq(1, 2, 3)
Iterable.fill(n)(letters).foldLeft(List(List[Int]())) { (a, b) =>
for (a<-a;b<-b) yield(b::a)
}
The need for the extra type annotation is a little annoying but it will not work without it (unless someone knows another way).
Another solution:
val alph = List("a", "b", "c")
val n = 3
alph.flatMap(List.fill(alph.size)(_))
.combinations(n)
.flatMap(_.permutations).toList
Update: If you want to get a list of strings in the output, then alph should be a string.
val alph = "abcd"