How to create listBuffer in collect function - scala

I tought that List is enough but I need to add element to my list.
I've tried to put this in ListBuffer constructor but without result.
var leavesValues: ListBuffer[Double] =
leaves
.collect { case leaf: Leaf => leaf.value.toDouble }
.toList
Later on I'm going to add value to my list so my expected output is mutable list.
Solution of Raman Mishra
But what if I need to append single value to the end of leavesValues
I can reverse but it's not good enough
I can use ListBuffer like below but I believe that there is cleaner solution:
val leavesValues: ListBuffer[Double] = ListBuffer()
leavesValues.appendAll(leaves
.collect { case leaf: Leaf => leaf.value.toDouble }
.toList)

case class Leaf(value:String)
val leaves = List(Leaf("5"), Leaf("6"), Leaf("7"), Leaf("8") ,Leaf("9") )
val leavesValues: List[Double] =
leaves
.collect { case leaf: Leaf => leaf.value.toDouble }
val value = Leaf("10").value.toDouble
val answer = value :: leavesValues
println(answer)
you can do it like this after getting the list of leavesValues you can prepand the value you want to add into the list.

Related

groupBy on List as LinkedHashMap instead of Map

I am processing XML using scala, and I am converting the XML into my own data structures. Currently, I am using plain Map instances to hold (sub-)elements, however, the order of elements from the XML gets lost this way, and I cannot reproduce the original XML.
Therefore, I want to use LinkedHashMap instances instead of Map, however I am using groupBy on the list of nodes, which creates a Map:
For example:
def parse(n:Node): Unit =
{
val leaves:Map[String, Seq[XmlItem]] =
n.child
.filter(node => { ... })
.groupBy(_.label)
.map((tuple:Tuple2[String, Seq[Node]]) =>
{
val items = tuple._2.map(node =>
{
val attributes = ...
if (node.text.nonEmpty)
XmlItem(Some(node.text), attributes)
else
XmlItem(None, attributes)
})
(tuple._1, items)
})
...
}
In this example, I want leaves to be of type LinkedHashMap to retain the order of n.child. How can I achieve this?
Note: I am grouping by label/tagname because elements can occur multiple times, and for each label/tagname, I keep a list of elements in my data structures.
Solution
As answered by #jwvh I am using foldLeft as a substitution for groupBy. Also, I decided to go with LinkedHashMap instead of ListMap.
def parse(n:Node): Unit =
{
val leaves:mutable.LinkedHashMap[String, Seq[XmlItem]] =
n.child
.filter(node => { ... })
.foldLeft(mutable.LinkedHashMap.empty[String, Seq[Node]])((m, sn) =>
{
m.update(sn.label, m.getOrElse(sn.label, Seq.empty[Node]) ++ Seq(sn))
m
})
.map((tuple:Tuple2[String, Seq[Node]]) =>
{
val items = tuple._2.map(node =>
{
val attributes = ...
if (node.text.nonEmpty)
XmlItem(Some(node.text), attributes)
else
XmlItem(None, attributes)
})
(tuple._1, items)
})
To get the rough equivalent to .groupBy() in a ListMap you could fold over your collection. The problem is that ListMap preserves the order of elements as they were appended, not as they were encountered.
import collection.immutable.ListMap
List('a','b','a','c').foldLeft(ListMap.empty[Char,Seq[Char]]){
case (lm,c) => lm.updated(c, c +: lm.getOrElse(c, Seq()))
}
//res0: ListMap[Char,Seq[Char]] = ListMap(b -> Seq(b), a -> Seq(a, a), c -> Seq(c))
To fix this you can foldRight instead of foldLeft. The result is the original order of elements as encountered (scanning left to right) but in reverse.
List('a','b','a','c').foldRight(ListMap.empty[Char,Seq[Char]]){
case (c,lm) => lm.updated(c, c +: lm.getOrElse(c, Seq()))
}
//res1: ListMap[Char,Seq[Char]] = ListMap(c -> Seq(c), b -> Seq(b), a -> Seq(a, a))
This isn't necessarily a bad thing since a ListMap is more efficient with last and init ops, O(1), than it is with head and tail ops, O(n).
To process the ListMap in the original left-to-right order you could .toList and .reverse it.
List('a','b','a','c').foldRight(ListMap.empty[Char,Seq[Char]]){
case (c,lm) => lm.updated(c, c +: lm.getOrElse(c, Seq()))
}.toList.reverse
//res2: List[(Char, Seq[Char])] = List((a,Seq(a, a)), (b,Seq(b)), (c,Seq(c)))
Purely immutable solution would be quite slow. So I'd go with
import collection.mutable.{ArrayBuffer, LinkedHashMap}
implicit class ExtraTraversableOps[A](seq: collection.TraversableOnce[A]) {
def orderedGroupBy[B](f: A => B): collection.Map[B, collection.Seq[A]] = {
val map = LinkedHashMap.empty[B, ArrayBuffer[A]]
for (x <- seq) {
val key = f(x)
map.getOrElseUpdate(key, ArrayBuffer.empty) += x
}
map
}
To use, just change .groupBy in your code to .orderedGroupBy.
The returned Map can't be mutated using this type (though it can be cast to mutable.Map or to mutable.LinkedHashMap), so it's safe enough for most purposes (and you could create a ListMap from it at the end if really desired).

Scala: AND conjunction in Lists

So I have result: List[List[Int]] = (List(0,1), List(0,1), List(1)) and I want to get the numbers every element of the the list has in common (in this case 1) like a logical AND conjunction. How can I do that?
Edit: If an element is empty it should return an empty List because there are no values every element has in common
Intuitive way
In each sublist, filter out the elements that are contained in all sublists, then flatten and remove duplicated:
val result1 = list.flatMap(_.filter(e => list.forall(_.contains(e)))).toSet
More efficient way
Find the smallest sublist and pick out elements that are in each sublist:
val result2 = list.minBy(_.size).filter(e => list.forall(_.contains(e))).toSet
Mathematical way
Turn each sublist into a set and intersect them:
val result3 = list.map(_.toSet).reduce(_.intersect(_))
You can do it with the intersect method:
def intersection(lists: List[List[Int]]): List[Int] = {
lists.headOption match {
case Some(head) =>
lists.foldLeft(head)((acc, l) => acc.intersect(l))
case None => Nil
}
The method may be more efficient if you use it with Set instead of List
The difficulty here is to do the intersect on the empty element, in this case Set.empty . to avoid this and solve the problem more functionally we can do this
def uniqueElements(reults:List[List[Int]]):Set[Int] = {
results match {
case head1::head2::tail => head1.toSet intersect head2.toSet intersect uniqueElements(tail)
case head::Nil => head.toSet
case Nil => Set.empty[Int]
}
}

Scala: Use map function tuples in a subsequent flatMap

I want to use a tuple of a map function in a subsequent flatMap.
Like this:
val list = orders.map(ord => (ord.prod.tasks, ord.quantity))
.zipWithIndex flatMap {
case (variable, index) =>
createSchedules(variable._1.size * variable._2, ord, null)
}
Is there a way in which I can use it or do you think that I have to change the way that I'm thinking about the solution?
I want to use a tuple of a map function in a subsequent flatMap.
Here's a working example of using tuples in a subsequent flatMap
val input = List("a", "b", "c")
val list = input.map(i => (i + "A", i + "B")).zipWithIndex flatMap{ case (variable, index)=> variable._1 + variable._2}
Issue with original code
val list = orders.map(ord => (ord.prod.tasks, ord.quantity)).zipWithIndex flatMap{case (variable, index)=>createSchedules(variable._1.size * variable._2, ord, null)}
One issue is that ord is out of scope in the 'createSchedules' call.
ord will only be visible in the initial 'orders.map' scope.
First of all, judging from the parameters that you're passing to createSchedules, it looks as if that function can be simplified to (I'm ignoring the null parameter for now):
def createSchedules(order: Order): List[Schedule] = {
val num = order.prod.tasks.size * order.quantity
// do stuff
}
Also, the initial map is unnecessary. list can be simplified to:
val list = orders.zipWithIndex
.flatMap {
case (order, index) =>
createSchedules(order)
}
It's unclear what you need the index for, though.

How to find tuple with different value in a list using scala?

I have following list:
val list = List(("name1",20),("name2",20),("name1",30),("name2",30),
("name3",40),("name3",30),("name3",20))
I want following output:
List(("name3",40))
I tried following:
val distElements = list.map(_._2).distinct
list.groupBy(_._1).map{ case(k,v) =>
val h = v.map(_._2)
if(distElements.equals(h)) List.empty else distElements.diff(h)
}.flatten
But this is not I am looking for.
Can anybody give answer/hint me to get expected output.
I understand the question as looking for the element of the list whose _2 (number) occurs only once.
val list = List(("name1",20),("name2",20),("name1",30),("name2",30),
("name3",40),("name3",30),("name3",20))
First you group by the _2 element, which gives you a map whose keys are lists of all elements with the same _2:
val g = list.groupBy(_._2) // Map[Int, List[(String, Int)]]
Now you can filter those entries that consists only of one element:
val opt = g.collectFirst { // Option[(String, Int)]
case (_, single :: Nil) => single
}
Or (if you are expecting possibly more than one distinct value)
val col = g.collect { // Map[String, Int]
case (_, single :: Nil) => single
}
Seems to me that you're looking to match against both the value of the left hand and the right hand at the same time while also preserving the type of collection you're looking at, a List. I would use collect:
val out = myList.collect{
case item # ("name3", 40) => item
}
which combines a PartialFunction with filter and map like qualities. In this case, it filters out any value for which the PartialFunction is not defined while mapping the values which match. Here, I've only allowed for a singular match.

how to flatten disjunction type

If I have a following method
def getMyList :\/[Throwable,List[\/[Throwable,Int]]] ={
....
}
how to flatten type of getMyList to \/[Throwable,List[Int]]
Just flatMap and sequenceU, it's all in scalaz:
def flatten(e: \/[Throwable,List[\/[Throwable,Int]]]): \/[Throwable,List[Int]] = {
e.flatMap(a => a.sequenceU)
}
If by flatten, you mean remove the left types from List[\/[Throwable,Int]], then you can map the outer disjunction, and collect the right types:
list.map(_.collect{ case \/-(x) => x})
I don't think that some higher order "flatten" exists for /. Looks like Validateion & ValidationNEL will be better choice for this problem. However here is "dirty" solution for /, it will return first fail. If you want to accumulate failures Validation is way to go
val getMyList: \/[Throwable,List[\/[Throwable,Int]]] =
//\/-(List(-\/(new RuntimeException("test")), \/-(1)))
\/-(List(\/-(2), \/-(1)))
val flatten = getMyList.fold(\/.left, _.foldLeft(\/.right[Throwable, List[Int]](List.empty[Int])) {
case (\/-(list), \/-(i)) => \/-(list :+ i)
case (\/-(list), -\/(err)) => -\/(err)
case (-\/(err), _) => -\/(err)
})
println(flatten)
We use the following method, where .sSuccess creates a \/[_, Seq[T]] and .sFail creates a \/[Throwable, _] with all of the throwables' error messages concatenated:
implicit class CondenseEither[T](seq: Seq[\/[Throwable,T]]) = {
def condenseSeq: \/[Throwable, Seq[T]] = {
val errs = seq.filter(_.isLeft).map(_.toEither)
if(errs.isEmpty) seq.map(_.toEither).map(_.right.get).sSuccess
else errs.map(_.left.get.getMessage).mkString(", ")).sFail
}
}
There's probably a way to do this without the toEithers