How would we iterate over two consecutive elements of a list and apply the difference function
For instance I have this :
val list = List(List("Eat", "Drink", "Sleep", "work"), List("Eat", "Sleep", "Dance"))
I want to iterate over these two consecutive elements and calculate the difference
I've tried this but I do not know how to iterate over each two consecutive elements
list.map((a,b) => a.diff(b))
the output should be List("Drink", "work")
If I understand correctly you probably want to iterate over a sliding window.
list.sliding(2).map{
case List(a, b) => a.diff(b)
case List(a) => a
}.toList
Alternatively you might also want grouped(2) which partitions the list into groups instead.
def main(args: Array[String]): Unit = {
val list = List(List("Eat", "Drink", "Sleep", "work"), List("Eat", "Sleep", "Dance"));
val diff = list.head.diff(list(1))
println(diff)
}
In your case, match can work perfectly fine:
val list = List(List("Eat", "Drink", "Sleep", "work"), List("Eat", "Sleep", "Dance"))
list match { case a :: b :: Nil => a diff b}
If the list does not always have 2 items, you should also have a catch-all case in match
Related
I have a below list
val list = listOf("o=one", "t=two", "t=two", "f=four", "o=one", "t=two", "s=seven", "o=one")
I wanna split it into list of the list contains [["o=one", "t=two", "t=two", "f=four"],["o=one", "t=two", "s=seven"],["o=one"]]
Actually I want to group list by "o=" delimiter and the list will always have at least one "0=" value. How could I achieve this in Kotlin without creating mutable var keyword because of my code should be in the functional style?
I have tried with group() and groupBy{} methods but couldn't get the expected result.
This might not cover all edge cases, and there might be better ways of doing it, but given that your requirements are not fully clear and extremely contrived, this should get you started either way. From here on out you can polish it yourself.
// Identifies indices of all "o=" elements in the list.
val splitAt = list
.withIndex()
.filter { it.value.startsWith( "o=" ) }
.map { it.index }
// Create sublists.
val split = splitAt
.windowed( 2, step = 1, partialWindows = true )
.map {
list.subList(
it[0],
if (it.count() == 1) it[0] + 1 else it[1]
)
}
I have questions regarding pattern matching of list prefixes (i.e. the first couple of elements of a list).
This compiles, but it does not work as expected:
val l = List(1,2,3)
val test = { m: List[Int] =>
m match {
case l :: tail => println("tail: "+tail.mkString(","))
case _ => println("no match")
}
}
test(List(1,2,3,4,5))
Output is tail: 2,3,4,5. I'd expect it to say either tail: 4,5, or to fail to match, or to fail at compile time. What makes this work as it does?
My second question is: How can I match a list prefix using a list? I know that this works as I expect:
case 1 :: 2 :: 3 :: tail => println("tail: "+tail.mkString(","))
I have, however, my prefixes as lists, and cannot hard-code them. Is pattern matching even the right thing here?
I know I could do something like
if (m startsWith l) {
val tail = m drop l.size
}
But a long list of these seems rather inelegant, especially in Scala.
Regarding the output of your first code snippet, the l inside of the match is actually a new value that shadows the outer scoped l and captures 1 during execution.
The problem you are encountering is that :: is the unapply for List to break it into exactly a single head value and the tail, deconstructing the linked list.
While there is a ::: operation to go along with ::: to concatenate two lists, it does not have a corresponding unapply which would let you use it in a pattern match in the way you desire.
I don't think this is possible. Closest syntax i could propose based on this workaround:
import collection.SeqLike
implicit class PrefixMatcher[T](prefix: Seq[T]) {
object then {
def unapply[S <: SeqLike[T,S]](seq: S): Option[S] =
if (seq startsWith prefix) Some(seq drop prefix.length) else None
}
}
Then you could use it as
val test: List[Int] => Unit = {
case l.then(tail) => println("tail: " + tail.mkString(","))
case _ => println("no match")
}
Addresing the first part of your question: As #Arne mentions, case l is not being matched against your list, but captures a new list. For the former you'd need to enclose it on backticks, but even then I don't see how you can achieve what you want, the closes I can think of is:
case `l` :+ x => println(s"tail is $s") //but this just works when the tail is just one element long.
For the final part of your question, maybe pattern matching is not the right thing to do here, how about:
val prefixes = List (List (1,2,3), List(4,5,6)...)
def findTail(li:List[Int]) = prefixes.collectFirst{ case p if li.startsWith(p) => li.drop(p.size) } //Option[List[Int]]
This will find the tail for the first prefix that matches the testList or None if there was no match.You can easily generalize it to work with more than just Ints.
I have following lists-
A = List(("192.168.20.1", "WinInfra", List("naa.6d867d9c7ac")),
("192.168.20.1", "TriSQLFreshInstall", List("naa.6d867d",
"naa.42704fdc4")),
("192.168.20.1", "redHat7", List("naa.4270cdf",
"naa.427045dc")))
B = List("4270cdf", "427045dc", "42704fdc4")
I want to check if last element of list A (it is a list of strings) contains any substring from list B and get output as unmatched elements only.
Edit: I want to check if any element of list B is exist in list A and collect only such list elements from list A which do not contains list B elements.
I want following output-
List(("192.168.20.1","WinInfra",List( "naa.6d867d9c7ac")))
How do I get above output using scala??
I think something like this:
A.filterNot(a => B.exists(b => a._3.exists(str => str.contains(b))))
or
A.filterNot(a => a._3.exists(str => B.exists(b => str.contains(b))))
or shorter, but less readable
A.filterNot(_._3 exists (B exists _.contains))
First, I wouldn't pass around tuples. It would be a lot easier if you would put this data structure into an object and work with that. However, it would be easier start by finding matches first. So you'll start out by applying a filter on List A:
A.filter { (ip, disc, sublist) => .... }
Where items in your sublist items are in List B:
sublist.exists(sublistItem => b.contains(sublistItem.replaceAll("naa.", "")))
This returns:
res1: List[(String, String, List[String])] = List((192.168.20.1,TriSQLFreshInstall,List(naa.6d867d, naa.42704fdc4)), (192.168.20.1,redHat7,List(naa.4270cdf, naa.427045dc)))
Which is the opposite of what you want. This is easy to correct by saying filterNot:
A.filterNot { (ip, disc, sublist) => sublist.exists(sublistItem => b.contains(sublistItem.replaceAll("naa.", ""))) }
I have two lists in Scala, how to merge them such that the tuples are grouped together?
Is there an existing Scala list API which can do this or need I do it by myself?
Input:
List((a,4), (b,1), (c,1), (d,1))
List((a,1), (b,1), (c,1))
Expected output:
List((a,5),(b,2),(c,2),(d,1))
You can try the following one-line:
scala> ( l1 ++ l2 ).groupBy( _._1 ).map( kv => (kv._1, kv._2.map( _._2).sum ) ).toList
res6: List[(Symbol, Int)] = List(('a,5), ('c,2), ('b,2), ('d,1))
Where l1 and l2 are the lists of tuples you want merge.
Now, the breakdown:
(l1 ++ l2) you just concatenate both lists
.groupBy( _._1) you group all tuples by their first element. You will receive a Map with
the first element as key and lists of tuples starting with this element as values.
.map( kv => (kv._1, kv._2.map( _._2).sum ) ) you make a new map, with similar keys, but the values are the sum of all second elements.
.toList you convert the result back to a list.
Alternatively, you can use pattern matching to access the tuple elements.
( l1 ++ l2 ).groupBy( _._1 ).map{
case (key,tuples) => (key, tuples.map( _._2).sum )
}.toList
Alternatively you can also use mapValues to shorten the code.
mapValues, as you can probably guess, allows you to re-map just the value for each (key, value) pair in the Map created by groupBy.
In this case the function passed to mapValues reduces each (Char, Int) tuple to just the Int then sums the resulting List of Ints.
(l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList
If the order of the output list needs to follow your example, just add sorted which relies on an Ordering[(Char, Int)] implicit instance.
(l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList.sorted
If you can assume that both List[(A,B)] are ordered according to Ordering[A], you could write something like:
def mergeLists[A,B](one:List[(A,B)], two:List[(A,B)])(op:(B,B)=>B)(implicit ord:Ordering[A]): List[(A,B)] = (one,two) match {
case (xs, Nil) => xs
case (Nil, ys) => ys
case((a,b)::xs,(aa,bb)::ys) =>
if (a == aa) (a, op(b,bb)) :: mergeLists(xs,ys)(op)(ord)
else if (ord.lt(a,aa)) (a, b) :: mergeLists(xs, (aa,bb)::ys)(op)(ord)
else (aa, bb) :: mergeLists((a,b) :: xs, ys)(op)(ord)
}
Unfortunately this isn't tail recursive.
Using foldLeft and toMap:
Get a map out of one list, and iterate through the second list. We upsert entries into the map.
l1.foldLeft(l2.toMap)((accumulator, tuple) =>
accumulator + (tuple._1 -> (accumulator.getOrElse(tuple._1, 0) + tuple._2))
).toList
results into:
List((Symbol(a),5), (Symbol(b),2), (Symbol(c),2), (Symbol(d),1))
Explanation:
l2.toMap converts List((a,1), (b,1), (c,1)) into immutable.Map(a->1, b->1, c->1)
foldLeft iterates through each tuple of list#1 l1.
(a,4) of list1 is added to the generated map, resulting to immutable.Map(a->1+4, b->1, c->1)
(b,1) of list1 is added to the generated map, resulting to immutable.Map(a->5, b->2, c->1)
(c,1) of list1 is added to the generated map, resulting to immutable.Map(a->5, b->2, c->2)
(d,1) of list1 is added to the generated map, resulting to immutable.Map(a->5, b->2, c->2, d->1)
toList converts the map back to the original input form, i.e. List[(Symbol, Int)]
This is the problem that I did solve, however being a total imperative Scala noob, I feel I found something totally not elegant. Any ideas of improvement appreciated.
val l1 = 4 :: 1 :: 2 :: 3 :: 4 :: Nil // original list
val insert = List(88,99) // list I want to insert on certain places
// method that finds all indexes of a particular element in a particular list
def indexesOf(element:Any, inList:List[Any]) = {
var indexes = List[Int]()
for(i <- 0 until inList.length) {
if(inList(i) == element) indexes = indexes :+ i
}
indexes
}
var indexes = indexesOf(4, l1) // get indexes where 4 appears in the original list
println(indexes)
var result = List[Any]()
// iterate through indexes and insert in front
for(i <- 0 until indexes.length) {
var prev = if(i == 0) 0 else indexes(i-1)
result = result ::: l1.slice(prev, indexes(i)) ::: insert
}
result = result ::: l1.drop(indexes.last) // append the last bit from original list
println(result)
I was thinking more elegant solution would be achievable with something like this, but that's just pure speculation.
var final:List[Any] = (0 /: indexes) {(final, i) => final ::: ins ::: l1.slice(i, indexes(i))
def insert[A](xs: List[A], extra: List[A])(p: A => Boolean) = {
xs.map(x => if (p(x)) extra ::: List(x) else List(x)).flatten
}
scala> insert(List(4,1,2,3,4),List(88,99)){_ == 4}
res3: List[Int] = List(88, 99, 4, 1, 2, 3, 88, 99, 4)
Edit: explanation added.
Our goal here is to insert a list (called extra) in front of selected elements in another list (here called xs--commonly used for lists, as if one thing is x then lots of them must be the plural xs). We want this to work on any type of list we might have, so we annotate it with the generic type [A].
Which elements are candidates for insertion? When writing the function, we don't know, so we provide a function that says true or false for each element (p: A => Boolean).
Now, for each element in the list x, we check--should we make the insertion (i.e. is p(x) true)? If yes, we just build it: extra ::: List(x) is just the elements of extra followed by the single item x. (It might be better to write this as extra :+ x--add the single item at the end.) If no, we have only the single item, but we make it List(x) instead of just x because we want everything to have the same type. So now, if we have something like
4 1 2 3 4
and our condition is that we insert 5 6 before 4, we generate
List(5 6 4) List(1) List(2) List(3) List(5 6 4)
This is exactly what we want, except we have a list of lists. To get rid of the inner lists and flatten everything into a single list, we just call flatten.
The flatten trick is cute, I wouldn't have thought of using map here myself. From my perspective this problem is a typical application for a fold, as you want go through the list and "collect" something (the result list). As we don't want our result list backwards, foldRight (a.k.a. :\) is here the right version:
def insert[A](xs: List[A], extra: List[A])(p: A => Boolean) =
xs.foldRight(List[A]())((x,xs) => if (p(x)) extra ::: (x :: xs) else x :: xs)
Here's another possibility, using Seq#patch to handle the actual inserts. You need to foldRight so that later indices are handled first (inserts modify the indices of all elements after the insert, so it would be tricky otherwise).
def insert[A](xs: Seq[A], ys: Seq[A])(pred: A => Boolean) = {
val positions = xs.zipWithIndex filter(x => pred(x._1)) map(_._2)
positions.foldRight(xs) { (pos, xs) => xs patch (pos, ys, 0) }
}