Scala check value inside mapping - scala

Allright so i don't know if this is possible, but let's say we have the following list:
List(1, 2, 3, 1)
If i want to apply a map over this, is there a way for me to check if i've already had a value before, e.g. on the 4th value (the 2nd 1) it'll say that it already came across the 1 and then throw an error or something.

This would be the role of a foldLeft stage:
List(1, 2, 3, 1).foldLeft(List[Int]()) {
// The item has already been encountered:
case (uniqueItems, b) if uniqueItems.contains(b) => {
// If as stated, you want to throw an exception, that's where you could do it
uniqueItems
}
// New item not seen yet:
case (uniqueItems, b) => uniqueItems :+ b
}
foldLeft traverses a sequence while working (at each new element), with a result based on the previous ones.
For each element, the pattern matching (uniqueItems, b) should be understood this way: uniqueItems is the "accumulator" (it's initialized as List[Int]()) and will be updated (or not) for each item of the list. And b if the new item of the list which is currently being processed.
By the way, this example is a (non-efficient) distinct over a list.

List(1, 2, 3, 1).distinct.map (n => n*n)
// res163: List[Int] = List(1, 4, 9)
This code removes duplicates, then performs the mappings in a self documenting, brief manner.

fold is probably the way to go. The problem is that each iteration has to carry both the memory of previous elements as well as the map() results as it is being built.
List(1, 2, 3, 11).foldRight((Set[Int](),List[String]())) {case (i, (st, lst)) =>
if (st(i)) throw new Error //duplicate encountered
else (st + i, i.toString :: lst) //add to memory and map result
}._2 //pull the map result from the tuple

Related

Scala pattern match not working on Stream?

I do a group-by and I get key -> Stream(of values)
I then need to do a pattern match on the stream collection to access the last item
but the pattern match doesn't work.
When I manually build the list of values using Seq the same pattern match code works
So my question is there a way to convert the Stream to Seq or List?
The IDE says toSeq is redundant
When I manually build the list of values using Seq the same pattern match code works
In scala 2.12, Seq (or sequence) are defaulted to List, see this question:
scala> Seq(1,2,3)
res3: Seq[Int] = List(1, 2, 3)
This is probably why the pattern matching works on your sequence (which happens to be a List) but not an a Stream, see this question.
The IDE says toSeq is redundant
Stream are indeed Seq:
Stream(1,2,3).toSeq
res4: collection.immutable.Seq[Int] = Stream(1, 2, 3)
So my question is there a way to convert the Stream to Seq or List?
To transform a Stream into a List, you can call the .toList method:
Stream(1,2,3).toList
res5: List[Int] = List(1, 2, 3)
Or with this Answer you don't have to transform to List:
val n: Seq[Any] = Stream(..)
n match {
case Nil => "Empty"
case h :: t => "Non-empty"
case h #:: t => "Non-empty stream"
}
For Stream, the concat symbol should be #::, the pattern match should like:
Make sure you reverse the Stream - so you get the last element, here an example:
n.reverse match {
case Nil => "Empty"
case h #:: _ => s"last element is $h"
}
Check it here ScalaFiddle

Scala - create a new list and update particular element from existing list

I am new to Scala and new OOP too. How can I update a particular element in a list while creating a new list.
val numbers= List(1,2,3,4,5)
val result = numbers.map(_*2)
I need to update third element only -> multiply by 2. How can I do that by using map?
You can use zipWithIndex to map the list into a list of tuples, where each element is accompanied by its index. Then, using map with pattern matching - you single out the third element (index = 2):
val numbers = List(1,2,3,4,5)
val result = numbers.zipWithIndex.map {
case (v, i) if i == 2 => v * 2
case (v, _) => v
}
// result: List[Int] = List(1, 2, 6, 4, 5)
Alternatively - you can use patch, which replaces a sub-sequence with a provided one:
numbers.patch(from = 2, patch = Seq(numbers(2) * 2), replaced = 1)
I think the clearest way of achieving this is by using updated(index: Int, elem: Int). For your example, it could be applied as follows:
val result = numbers.updated(2, numbers(2) * 2)
list.zipWithIndex creates a list of pairs with original element on the left, and index in the list on the right (indices are 0-based, so "third element" is at index 2).
val result = number.zipWithIndex.map {
case (n, 2) => n*2
case n => n
}
This creates an intermediate list holding the pairs, and then maps through it to do your transformation. A bit more efficient approach is to use iterator. Iterators a 'lazy', so, rather than creating an intermediate container, it will generate the pairs one-by-one, and send them straight to the .map:
val result = number.iterator.zipWithIndex.map {
case (n, 2) => n*2
case n => n
}.toList
1st and the foremost scala is FOP and not OOP. You can update any element of a list through the keyword "updated", see the following example for details:
Signature :- updated(index,value)
val numbers= List(1,2,3,4,5)
print(numbers.updated(2,10))
Now here the 1st argument is the index and the 2nd argument is the value. The result of this code will modify the list to:
List(1, 2, 10, 4, 5).

scala set empty rows in list, set empty value

I have a question about setting the result to Empty (i.e. nothing), I've researched in past questions and did not find a good solution.
The question is quite simple, say I have a List of Int and List of Bool
val a = List(1,2,3,4,5)
val b = List(F,T,T,F,F)
and I want to zip them and do some mapping:
val result = (a,b).zipped.map((x,y)=>(if(b) a else ())
I assume I am doing the right thing above which takes each element of a and b and does the operation where if b is true, return a, else return nothing. I expect the result to only have the numbers (2,3). However, my Eclipse seems to indicate that the result generated is List[AnyVal] instead of List[Int].
I have tested the same setting, but using Lists, and when I set b to List(), the process works and Eclipse is understanding that I want to set an empty List, so I am lost where I when wrong..
Thanks in advance
You want val result = (a zip b).filter(_._2).map(_._1). map never filters, so don't attempt to return () from its argument and hope it will filter.
val a = List(1, 2, 3, 4, 5)
val b = List(false, true, true, false, false)
a.zip(b) // zip two lists
.filter(_._2) // filter if second element is true
.map(_._1) // grab first element of tuple
// List[Int] = List(2, 3)
You could also use collect, which will keep only the elements that match a pattern. You can think of it like a map that discards anything it's not defined for.
val a = List(1, 2, 3, 4, 5)
val b = List(false, true, true, false, false)
(a zip b) collect { case (x, true) => x }

Group by margin

I am having a sequence of Int numbers:
val numbers = Seq(5, 3, 4, 1)
I need to group them according to their difference. The difference has to be smaller or equal to a certain threshold, let it be 2 for this example. So the possible groups would be:
(5, 3, 4) (1)
(1, 3) (5, 4)
I don't really care which of these constellations of groups I'll get. Each element is allowed to be used once. I also need to remain the index, so prior grouping I would need a zipWithIndex.
Is there a clever way to do such grouping?
Ok then. Idea of the algorithm:
Take the next element in numbers. Check whether it belongs to a previously created group. If it does, add it to that group. If not, add a new group with the element.
I use IndexedSeq because i want indexing to be O(1).
It is kinda long, but I can't think of something better at the moment. I hope I understood you correctly with your idea of "difference".
val numbers = Seq(5, 3, 4, 1)
def group(seq: Seq[Int], treshold: Int) = seq.zipWithIndex.foldLeft(IndexedSeq.empty[IndexedSeq[(Int,Int)]])((result, elem) => {
(0 until result.size).find(
i => result(i).forall(num => (num._1 - elem._1).abs <= treshold)).map(
i => result.updated(i, result(i) :+ elem))
.getOrElse(result :+ IndexedSeq(elem))
})
println(group(numbers, 2)) //result Vector(Vector((5,0), (3,1), (4,2)), Vector((1,3)))
Edit forgot you wanted to zipWithIndex
Since you're working with indices of elements anyway, you may not care about working with indices of the groups as well, in which case Kigyo's answer is probably the right one.
One of the nice things about functional programming is that it can often free you from working with indices, though, so for the sake of completeness, here's an implementation using span that doesn't need to track the indices of groups (first for the simple form without element indices):
val numbers = Seq(5, 3, 4, 1)
numbers.foldLeft(List.empty[List[Int]]) {
case (acc, x) => acc.span(_.exists(y => math.abs(x - y) > 2)) match {
case (bad, picked :: rest) => (x :: picked) :: rest ::: bad
case (bad, _) => List(x) :: bad
}
}
If you haven't already zipWithIndex-ed numbers, you can also take care of that during the fold without too much extra fuss:
val numbers = Seq(5, 3, 4, 1)
numbers.foldLeft(List.empty[List[(Int, Int)]], 0) {
case ((acc, i), x) => acc.span(_.exists(y => math.abs(x - y._1) > 2)) match {
case (bad, picked :: rest) => (((x, i) :: picked) :: rest ::: bad, i + 1)
case (bad, _) => (List((x, i)) :: bad, i + 1)
}
}._1
This returns List(List((1, 3)), List((4, 2), (3, 1), (5, 0))) as expected, and saves you an iteration through the sequence with very little extra verbosity.

Scala - finding a specific tuple in a list

Let's say we have this list of tuples:
val data = List(('a', List(1, 0)), ('b', List(1, 1)), ('c', List(0)))
The list has this signature:
List[(Char, List[Int])]
My task is to get the "List[Int]" element from a tuple inside "data" whose key is, for instance, letter "b". If I implement a method like "findIntList(data, 'b')", then I expect List(1, 1) as a result. I have tried the following approaches:
data.foreach { elem => if (elem._1 == char) return elem._2 }
data.find(x=> x._1 == ch)
for (elem <- data) yield elem match {case (x, y: List[Bit]) => if (x == char) y}
for (x <- data) yield if (x._1 == char) x._2
With all the approaches (except Approach 1, where I employ an explicit "return"), I get either a List[Option] or List[Any] and I don't know how to extract the "List[Int]" out of it.
One of many ways:
data.toMap.get('b').get
toMap converts a list of 2-tuples into a Map from the first element of the tuples to the second. get gives you the value for the given key and returns an Option, thus you need another get to actually get the list.
Or you can use:
data.find(_._1 == 'b').get._2
Note: Only use get on Option when you can guarantee that you'll have a Some and not a None. See http://www.scala-lang.org/api/current/index.html#scala.Option for how to use Option idiomatic.
Update: Explanation of the result types you see with your different approaches
Approach 2: find returns an Option[List[Int]] because it can not guarantee that a matching element gets found.
Approach 3: here you basically do a map, i.e. you apply a function to each element of your collection. For the element you are looking for the function returns your List[Int] for all other elements it contains the value () which is the Unit value, roughly equivalent to void in Java, but an actual type. Since the only common super type of ´List[Int]´ and ´Unit´ is ´Any´ you get a ´List[Any]´ as the result.
Approach 4 is basically the same as #3
Another way is
data.toMap.apply('b')
Or with one intermediate step this is even nicer:
val m = data.toMap
m('b')
where apply is used implicitly, i.e., the last line is equivalent to
m.apply('b')
There are multiple ways of doing it. One more way:
scala> def listInt(ls:List[(Char, List[Int])],ch:Char) = ls filter (a => a._1 == ch) match {
| case Nil => List[Int]()
| case x ::xs => x._2
| }
listInt: (ls: List[(Char, List[Int])], ch: Char)List[Int]
scala> listInt(data, 'b')
res66: List[Int] = List(1, 1)
You can try something like(when you are sure it exists) simply by adding type information.
val char = 'b'
data.collect{case (x,y:List[Int]) if x == char => y}.head
or use headOption if your not sure the character exists
data.collect{case (x,y:List[Int]) if x == char => y}.headOption
You can also solve this using pattern matching. Keep in mind you need to make it recursive though. The solution should look something like this;
def findTupleValue(tupleList: List[(Char, List[Int])], char: Char): List[Int] = tupleList match {
case (k, list) :: _ if char == k => list
case _ :: theRest => findTupleValue(theRest, char)
}
What this will do is walk your tuple list recursively. Check whether the head element matches your condition (the key you are looking for) and then returns it. Or continues with the remainder of the list.