folding in scala clarification - scala

Want to improve piece of code by folding operation
this code works
var r = 0
arr.foreach { s =>
val count = arr.count(_ == s)
if (count > r) r = count
}
But in case with fold. I have an error with types.
Error:(44, 49) type mismatch;
found : (Int, String) => Any
required: (Any, Any) => Any
arr.fold(0: Int)((result: Int, s: String) => {
^
arr.fold(0: Int)((result: Int, s: String) => {
val count = arr.count(_ == s)
if (count > result) count
})
What is wrong ?

If you're trying to find the string with most occurrences in the the array, you can use groupBy:
val arr = Array("yuval", "yuval", "david")
arr.groupBy(identity).mapValues(_.length).max._2
arr: Array[String] = Array(yuval, yuval, david)
res1: Int = 2
If you really want to jump to hoops of using fold, you'll either need foldLeft or foldRight which pass the accumulator along for each value in the sequence:
val arr = Array("yuval", "yuval", "david")
val aggregatedMap = arr.foldLeft(Map.empty[String, Int].withDefaultValue(0)) {
case (map, value) => map.updated(value, map(value) + 1)
}
println(aggregatedMap.max._2)

Related

how to get the index of the duplicate pair in the a list using scala

I have a Scala list below :
val numList = List(1,2,3,4,5,1,2)
I want to get index of the same element pair in the list. The output should look like (0,5),(1,6)
How can I achieve using map?
def catchDuplicates(num : List[Int]) : (Int , Int) = {
val count = 0;
val emptyMap: HashMap[Int, Int] = HashMap.empty[Int, Int]
for (i <- num)
if (emptyMap.contains(i)) {
emptyMap.put(i, (emptyMap.get(i)) + 1) }
else {
emptyMap.put(i, 1)
}
}
Let's make the challenge a little more interesting.
val numList = List(1,2,3,4,5,1,2,1)
Now the result should be something like (0, 5, 7),(1, 6), which makes it pretty clear that returning one or more tuples is not going to be feasible. Returning a List of List[Int] would make much more sense.
def catchDuplicates(nums: List[Int]): List[List[Int]] =
nums.zipWithIndex //List[(Int,Int)]
.groupMap(_._1)(_._2) //Map[Int,List[Int]]
.values //Iterable[List[Int]]
.filter(_.lengthIs > 1)
.toList //List[List[Int]]
You might also add a .view in order to minimize the number of traversals and intermediate collections created.
def catchDuplicates(nums: List[Int]): List[List[Int]] =
nums.view
.zipWithIndex
.groupMap(_._1)(_._2)
.collect{case (_,vs) if vs.sizeIs > 1 => vs.toList}
.toList
How can I achieve using map?
You can't.
Because you only want to return the indexes of the elements that appear twice; which is a very different kind of transformation than the one that map expects.
You can use foldLeft thought.
object catchDuplicates {
final case class Result[A](elem: A, firstIdx: Int, secondIdx: Int)
private final case class State[A](seenElements: Map[A, Int], duplicates: List[Result[A]]) {
def next(elem: A, idx: Int): State[A] =
seenElements.get(key = elem).fold(
ifEmpty = this.copy(seenElements = this.seenElements + (elem -> idx))
) { firstIdx =>
State(
seenElements = this.seenElements.removed(key = elem),
duplicates = Result(elem, firstIdx, secondIdx = idx) :: this.duplicates
)
}
}
private object State {
def initial[A]: State[A] =
State(
seenElements = Map.empty,
duplicates = List.empty
)
}
def apply[A](data: List[A]): List[Result[A]] =
data.iterator.zipWithIndex.foldLeft(State.initial[A]) {
case (acc, (elem, idx)) =>
acc.next(elem, idx)
}.duplicates // You may add a reverse here if order is important.
}
Which can be used like this:
val numList = List(1,2,3,4,5,1,2)
val result = catchDuplicates(numList)
// result: List[Result] = List(Result(2,1,6), Result(1,0,5))
You can see the code running here.
I think returning tuple is not a good option instead you should try Map like -
object FindIndexOfDupElement extends App {
val numList = List(1, 2, 3, 4, 5, 1, 2)
#tailrec
def findIndex(elems: List[Int], res: Map[Int, List[Int]] = Map.empty, index: Int = 0): Map[Int, List[Int]] = {
elems match {
case head :: rest =>
if (res.get(head).isEmpty) {
findIndex(rest, res ++ Map(head -> (index :: Nil)), index + 1)
} else {
val updatedMap: Map[Int, List[Int]] = res.map {
case (key, indexes) if key == head => (key, (indexes :+ index))
case (key, indexes) => (key, indexes)
}
findIndex(rest, updatedMap, index + 1)
}
case _ => res
}
}
println(findIndex(numList).filter(x => x._2.size > 1))
}
you can clearly see the number(key) and respective index in the map -
HashMap(1 -> List(0, 5), 2 -> List(1, 6))

Illegal start of declaration

I have the following Scala code:
object Solution {
def main(args: Array[String]) {
val List(n, m) = readLine().split(" ").map(_.toInt).toList;
val knowledge: List[Set[Int]] = (0 until n).map( _ => {
val knows: List[Char] = readLine().toList;
(0 until m).toSet.flatMap(topic: Int => {
knows(topic) match {
case '1' => Set(topic);
case _ => Set[Int].empty;
}
});
}).toList;
val teams: List[Int] = knowledge.grouped(2).map{ case(x, y) => x ++ y }.map(_.size);
val best: Int = teams.max;
val count = teams.filter(_ == max);
println(best + " " + count);
}
}
On it, I am getting this error:
Solution.scala:16: error: illegal start of declaration
knows(topic) match {Solution.scala:
On this line:
knows(topic) match {
I can not understand what is wrong.
I am using a match in a body of a flatMap.
Any idea what is wrong with this code?
with the explicit type, you'll need
(topic: Int) =>
as per the syntax for an anonymous function
https://www.scala-lang.org/old/node/133
According to this answer, you can also move the type to
(0 until m).toSet[Int].flatMap(topic => {

concateneate lines having same title

I have the following issue
Given this list in input , I want to concateneate integers for each line having the same title,
val listIn= List("TitleB,Int,11,0",
"TitleB,Int,1,0",
"TitleB,Int,1,0",
"TitleB,Int,3,0",
"TitleA,STR,3,0",
"TitleC,STR,4,5")
I wrote the following function
def sumB(list: List[String]): List[String] = {
val itemPattern = raw"(.*)(\d+),(\d+)\s*".r
list.foldLeft(ListMap.empty[String, (Int,Int)].withDefaultValue((0,0))) {
case (line, stri) =>
val itemPattern(k,i,j) = stri
val (a, b) = line(k)
line.updated(k, (i.toInt + a, j.toInt + b))
}.toList.map { case (k, (i, j)) => s"$k$i,$j" }
}
Expected output would be:
List("TitleB,Int,16,0",
"TitleA,STR,3,0",
"TitleC,STR,4,5")
Since you are looking to preserve the order of the titles as they appear in the input data, I would suggest you to use LinkedHashMap with foldLeft as below
val finalResult = listIn.foldLeft(new mutable.LinkedHashMap[String, (String, String, Int, Int)]){ (x, y) => {
val splitted = y.split(",")
if(x.keySet.contains(Try(splitted(0)).getOrElse(""))){
val oldTuple = x(Try(splitted(0)).getOrElse(""))
x.update(Try(splitted(0)).getOrElse(""), (Try(splitted(0)).getOrElse(""), Try(splitted(1)).getOrElse(""), oldTuple._3+Try(splitted(2).toInt).getOrElse(0), oldTuple._4+Try(splitted(3).toInt).getOrElse(0)))
x
}
else {
x.put(Try(splitted(0)).getOrElse(""), (Try(splitted(0)).getOrElse(""), Try(splitted(1)).getOrElse(""), Try(splitted(2).toInt).getOrElse(0), Try(splitted(3).toInt).getOrElse(0)))
x
}
}}.mapValues(iter => iter._1+","+iter._2+","+iter._3+","+iter._4).values.toList
finalResult should be your desired output
List("TitleB,Int,16,0", "TitleA,STR,3,0", "TitleC,STR,4,5")

Index of List in Scala

I'm new in Scala.
For example, I have a List like
val s = List(5, 11, 15, 7)
and I need a lambda function to create a new list of the indexes of the elements that are more than 10. I can not use the language Scala libraries or functions. Only standard Scala opportunities.
How can I calculate the indices of these elements? Thank you!
Try this code:
val lambda = (list: List[Int]) => {
def filterList(l : List[Int], condition : Int, index: Int, acc: List[(Int, Int)]) : List[(Int, Int)] = l match {
case List() => acc
case h::tail =>
if (h > condition) filterList( tail, condition, index + 1, (index, h) :: acc )
else filterList( tail, condition, index + 1, acc )
}
filterList(list, 10, 0, List() )
}
val r = lambda( s )
Well... there are may ways to solve this.
First lets see the more wide-spread "imperative"-like solution with var,
val lambda = (list: List[Int]) => {
var indexList = List.empty[Int]
var i = 0
for (elem <- list) {
if (elem > 10) indexList = i +: indexList
i = i + 1
}
indexList.reverse
}
Now... we can look at a bit more "functional-like" recursive approach,
val lambda = (list: List[Int]) => {
def _inner(list: List[Int], index: Int, indexList: List[Int]) = {
list match {
case Nil => indexList
case elem :: tail => {
if (elem > 10) _inner(tail, index + 1, index +: indexList)
else _inner(tail, index + 1, indexList)
}
}
}
_inner(list, 0, List.empty[Int]).reverse
}

count char frequency in string with scala

I search a way to count the different chars from a string.
The problem is that's not allowed to use any functions from the scala-api or to use vars (only val).
I want same result like that
val fruit: String = "dasisteintest"
println(fruit.groupBy(identity).mapValues(_.size))
Map(e -> 2, s -> 3, n -> 1, t -> 3, a -> 1, i -> 2, d -> 1)
In every try I made, I have at the end a list[(Char,Int)] where I have to change the Int. But because it's an immutable list I can't change it.
How can I implement that counting char algorithm?
You can use the following snippet:
val fruit: String = "dasisteintest"
val map = scala.collection.mutable.HashMap.empty[Char, Int]
for (symbol <- fruit) {
if (map.contains(symbol))
map(symbol) = map(symbol) + 1
else
map.+=((symbol, 1))
}
println(map)
def countChars(str: String) = {
def loop(chars: List[Char], acc: Map[Char, Int]): Map[Char, Int] = {
chars match {
case Nil => acc
case char :: rest =>
loop(rest, acc + (char -> (acc(char) + 1)))
}
}
loop(str.toList, Map.empty[Char, Int] withDefaultValue 0)
}
Test:
# val fruit: String = "dasisteintest"
fruit: String = "dasisteintest"
# countChars(fruit)
res4: Map[Char, Int] = Map('e' -> 2, 's' -> 3, 'n' -> 1, 't' -> 3, 'a' -> 1, 'i' -> 2, 'd' -> 1)
Whatever you use here is from scala api, a Map.apply, or Map.empty or List.::. It would be difficult not to use any functions from scala api. My guess is that you aren't supposed to be using things like groupBy and you are supposed to do something a bit more low level. Folding is natural solution here, like foldLeft, but if that is considered "using a function from scala api", you can just implement foldLeft yourself, just like I did in my solution.
As for withDefaultValue you can replace it with explicit check if value is present and put a 1 there in that case.
You don't know how to change an in a list or map that is immutable? You just make a new list with that value changed.
For a map, given
val map = Map('a' -> 3)
you can update it doing
# map.updated('a', 4)
res6: Map[Char, Int] = Map('a' -> 4)
or
# map + ('a' -> 4)
res7: Map[Char, Int] = Map('a' -> 4)
Both do exact the same thing - insert or update - and return new map.
Here you can find how to update elements in a list
Replace element in List with scala
although you rarely want to access list by index, you rather just build a new list from old one while iterating over it somehow, e.g. with fold.
What do you mean by "no functions from Scala API"? Does that include no functions from the collections api? If so, then ignore my answer. However if we can't even use reduce methods, I don't see the point of this exercise.
Here's what I came up with:
val fruit: String = "dasisteintest"
fruit.foldLeft[Map[Char,Int]](Map.empty)((map, c) => map + (c -> (map.getOrElse(c, 0) + 1)))
Although can you expand what you mean by ""you have to change the Int"?
Here is the expected code.
First the function which returns the char from the list
def removeFromList(l: List[Char], l2: List[Char], toDelete: Char): List[Char] = {
if (l.isEmpty) l2
else {
if (l.head == toDelete)
removeFromList(l.tail, l2, toDelete)
else
removeFromList(l.tail, l2 :+ l.head, toDelete)
}
}
and then the function which counts the chars and calls removeFromList()
def zaehlZeichen(s: String): List[(Char, Int)] = {
val sl: List[Char] = s.toUpperCase().toList
if (sl.isEmpty) Nil
else {
val l: List[Char] = List()
val tupleList: List[(Char, Int)] = List();
val in: Int = countChar(sl, 0, sl.head)
val t: List[(Char, Int)] = tupleList :+ (sl.head, in)
val cutL: List[Char] = removeFromList(sl, l, sl.head)
t ::: zaehlZeichen(cutL.mkString);
}
}
object CountEachCharInString
{
//Counts the number of a specific character in the Input String
def characterCount(inputChar: Char, str: String): Unit =
{
var num: Int = 0
num = str.count(_ == inputChar);
/*
//Implementation of count method
for(i <- 0 to str.length - 1)
{
if(str(i) == inputChar)
{
num += 1
}
}
*/
println(s"$inputChar appears $num times")
}
def countAllChars(inputStr: String): Unit =
{
//To eliminate duplicates, need one loop inside the other. str(i) == str(j)
for(i <- 0 to inputStr.length - 1)
{
var occurence: Int = 0
for(j <- 0 to i-1)
{
if(inputStr(j) == inputStr(i))
occurence = occurence + 1
}
if(occurence == 0)
{
characterCount(inputStr(i), inputStr)
//var num = inputStr.count(_ == inputStr(i))
//println( inputStr(i) + s" appears $num times")
}
}
}
def main(args: Array[String]): Unit =
{
countAllChars("HelloforHello...^^&&&&")
}
}