Modify a position in a (String, String) variable in Scala - scala

I have tuple separated by a coma that looks like this:
("TRN_KEY", "88.330000;1;2")
I would like to add some more info to the second position.
For example:
I would like to add ;99;99 to the 88.330000;1;2 so that at the end it would look like:
(TRN_KEY, 88.330000;1;2;99;99)

One way is to de-compose your tuple and concat the additional string to the second element:
object MyObject {
val (first, second) = ("TRN_KEY","88.330000;1;2")
(first, second + ";3;4"))
}
Which yields:
res0: (String, String) = (TRN_KEY,88.330000;1;2;3;4)
Another way to go is copy to tuple with the new value using Tuple2.copy, as tuples are immutable by design.

You can not modify the data in place as Tuple2 is immutable.
An option would be to have a var and then use the copy method.
In Scala due to structural sharing this is a rather cheap and fast operation.
scala> var tup = ("TRN_KEY","88.330000;1;2")
tup: (String, String) = (TRN_KEY,88.330000;1;2)
scala> tup = tup.copy(_2 = tup._2 + "data")
tup: (String, String) = (TRN_KEY,88.330000;1;2data)

Here is a simple function that gets the job done. It takes a tuple and appends a string to the second element of the tuple.
def appendTup(tup:(String, String))(append:String):(String,String) = {
(tup._1, tup._2 + append)
}
Here is some code using it
val tup = ("TRN_KEY", "88.330000;1;2")
val tup2 = appendTup(tup)(";99;99")
println(tup2)
Here is my output
(TRN_KEY,88.330000;1;2;99;99)

If you really want to make it mutable you could use a case class such as:
case class newTup(col1: String, var col2: String)
val rec1 = newTup("TRN_KEY", "88.330000;1;2")
rec1.col2 = rec1.col2 + ";99;99"
rec1
res3: newTup = newTup(TRN_KEY,88.330000;1;2;99;99)
But, as mentioned above, it would be better to use .copy

Related

Extend / Replicate Scala collections syntax to create your own collection?

I want to build a map however I want to discard all keys with empty values as shown below:
#tailrec
def safeFiltersMap(
map: Map[String, String],
accumulator: Map[String,String] = Map.empty): Map[String, String] = {
if(map.isEmpty) return accumulator
val curr = map.head
val (key, value) = curr
safeFiltersMap(
map.tail,
if(value.nonEmpty) accumulator + (key->value)
else accumulator
)
}
Now this is fine however I need to use it like this:
val safeMap = safeFiltersMap(Map("a"->"b","c"->"d"))
whereas I want to use it like the way we instantiate a map:
val safeMap = safeFiltersMap("a"->"b","c"->"d")
What syntax can I follow to achieve this?
The -> syntax isn't a special syntax in Scala. It's actually just a fancy way of constructing a 2-tuple. So you can write your own functions that take 2-tuples as well. You don't need to define a new Map type. You just need a function that filters the existing one.
def safeFiltersMap(args: (String, String)*): Map[String, String] =
Map(args: _*).filter {
result => {
val (_, value) = result
value.nonEmpty
}
}
Then call using
safeFiltersMap("a"->"b","c"->"d")

Build a class instance from elements in a list

I want to build an instance of a class Something by calling the function foo on this calss for every element in a list. e.g.
val list = List(1,2,3)
should result in a call with the same effect as:
val something = somethingBuilder.foo(1).foo(2).foo(3)
Is there a way to perform this?
As I understand your question, you can do :
val list = List(1,2,3)
val something = somethingBuilder
list.foreach(something.foo)
I am assuming that you care for the returned value of your builder call. Then following code will print Builder(6):
val list = List(1,2,3)
case class Builder(val i: Int){
def build(j: Int) = Builder(i+j)
}
val finalBuilder = list.foldLeft(Builder(0))(_.build(_))
println(finalBuilder)
If you only care for the side effect, maybe Rafael's solution is more adequate (although using the foldLeft will of course also trigger the side-effect).
This can be one of the solution.
val lst = List(1,2,3,4,5)
class Builder(val i: Int){
def Foo() {
println("Value is initialized: " + i)
}
}
lst.map( a => new Builder(a).Foo())
But the disclaimer, it does generates the list of empty lists as a side effect
print(lst.map( a => new Builder(a).Foo()))

Copy method(Scala Tuple) not working as desired

Going by the answer given here
You can't reassign tuple values. They're intentionally immutable: once you have created a tuple, you can be confident that it will never change. This is very useful for writing correct code!
But what if you want a different tuple? That's where the copy method comes in:
val tuple = (1, "test")
val another = tuple.copy(_2 = "new")
When I run the below code
var TupleOne=("One", 2, true)
println(TupleOne._1) //Gives One(As desired)
var TupleTwo=("Two", TupleOne.copy(_1 = "new"),false)
println(TupleTwo._2) //Gives (new,2,true)->weird
As per my understanding the second tuple should be ("Two","new",false) and printing TupleTwo._2 should give "new"
Why the behavior is different here?
TupleOne.copy(_1 = "new") is ("One", "new", true). So when you put it into a tuple, TupleTwo is ("Two", ("One", "new", true), false). And TupleTwo._2 is of course ("One", "new", true) again. (You don't get the quotes " when they are printed, because that's how toString on String is defined.)
1) You don't even need .copy based on your example.
you have
var TupleOne=("One", 2, true)
you are expecting
var TupleTwo = ("Two","new",false)
Theres nothing to be copied tupleOne.
The other thing is .copy returns type of instance that you are copying from, which is Tuple too
Example to copy first element but mutate second element.
scala> val tupleOne = ("copyme", "dont-copy-me")
tupleOne: (String, String) = (copyme,dont-copy-me)
scala> val tupleTwo = tupleOne.copy(_2 = "I'm new")
tupleTwo: (String, String) = (copyme,I'm new)
2) Lets go to your example,
scala> val TupleOne=("One",2,true)
TupleOne: (String, Int, Boolean) = (One,2,true)
To get your expected result, you have to alter each element of TupleOne as below which makes no sense because you are copying nothing.
scala> TupleOne.copy(_1= "Two", _2 = "new", _3= false)
res3: (String, String, Boolean) = (Two,new,false)
3) Again, since Tuple.copy returns Tuple, your TupleTwo is Tuple inside Tuple. (#2 is what you probably looking for)
scala> var TupleOne=("One",2,true)
TupleOne: (String, Int, Boolean) = (One,2,true)
scala> var TupleTwo=("Two",TupleOne.copy(_1 = "new"),false)
TupleTwo: (String, (String, Int, Boolean), Boolean) = (Two,(new,2,true),false)

Scala equivalent of Haskell's insertWith for Maps

I'm looking to do the simple task of counting words in a String. The easiest way I've found is to use a Map to keep track of word frequencies. Previously with Haskell, I used its Map's function insertWith, which takes a function that resolves key collisions, along with the key and value pair. I can't find anything similar in Scala's library though; only an add function (+), which presumably overwrites the previous value when re-inserting a key. For my purposes though, instead of overwriting the previous value, I want to add 1 to it to increase its count.
Obviously I could write a function to check if a key already exists, fetch its value, add 1 to it, and re-insert it, but it seems odd that a function like this isn't included. Am I missing something? What would be the Scala way of doing this?
Use a map with default value and then update with +=
import scala.collection.mutable
val count = mutable.Map[String, Int]().withDefaultValue(0)
count("abc") += 1
println(count("abc"))
If it's a string then why not use the split module
import Data.List.Split
let mywords = "he is a good good boy"
length $ nub $ splitOn " " mywords
5
If you want to stick with Scala's immutable style, you could create your own class with immutable semantics:
class CountMap protected(val counts: Map[String, Int]){
def +(str: String) = new CountMap(counts + (str -> (counts(str) + 1)))
def apply(str: String) = counts(str)
}
object CountMap {
def apply(counts: Map[String, Int] = Map[String, Int]()) = new CountMap(counts.withDefaultValue(0))
}
And then you can use it:
val added = CountMap() + "hello" + "hello" + "world" + "foo" + "bar"
added("hello")
>>2
added("qux")
>>0
You might also add apply overloads on the companion object so that you can directly input a sequence of words, or even a sentence:
object CountMap {
def apply(counts: Map[String, Int] = Map[String, Int]()): CountMap = new CountMap(counts.withDefaultValue(0))
def apply(words: Seq[String]): CountMap = CountMap(words.groupBy(w => w).map { case(word, group) => word -> group.length })
def apply(sentence: String): CountMap = CountMap(sentence.split(" "))
}
And then the you can even more easily:
CountMap(Seq("hello", "hello", "world", "world", "foo", "bar"))
Or:
CountMap("hello hello world world foo bar")

In Scala, how can I reassign tuple values?

I'm trying to do something like the following
var tuple = (1, "test")
tuple._2 = "new"
However this does not compile it complains about val
You can't reassign tuple values. They're intentionally immutable: once you have created a tuple, you can be confident that it will never change. This is very useful for writing correct code!
But what if you want a different tuple? That's where the copy method comes in:
val tuple = (1, "test")
val another = tuple.copy(_2 = "new")
or if you really want to use a var to contain the tuple:
var tuple = (1, "test")
tuple = tuple.copy(_2 = "new")
Alternatively, if you really, really want your values to change individually, you can use a case class instead (probably with an implicit conversion so you can get a tuple when you need it):
case class Doublet[A,B](var _1: A, var _2: B) {}
implicit def doublet_to_tuple[A,B](db: Doublet[A,B]) = (db._1, db._2)
val doublet = Doublet(1, "test")
doublet._2 = "new"
You can wrapper the component(s) you need to modify in a case class with a var member, like:
case class Ref[A](var value: A)
var tuple = (Ref(1), "test")
tuple._1.value = 2
println(tuple._1.value) // -> 2