Suppose I have a list of elements myl and a function f. I would like to chop up myl into a list of lists, where each new "sublist" contains a contiguous stretch of myl on which the value of f is constant.
For example, if myl = List( (1,2), (3,2), (4,1), (6,2) ) and def f(x: (Int, Int)) = x._2. Then the result should be List( List((1,2), (3,2)), List((4, 1)), List((6,2)) ).
Is there an elegant way to write such a function without any vars?
def groupBy[A](as: List[A])(p: (A, A) => Boolean): List[List[A]] =
as.foldRight(List.empty[List[A]]) {
case (x, (ys # y :: _) :: zs) if p(x, y) => (x :: ys) :: zs
case (x, ys) => List(x) :: ys
}
scala> groupBy(myl)(_._2 == _._2)
res0: List[List[(Int, Int)]] = List(List((1,2), (3,2)), List((4,1)), List((6,2)))
Edit: I also wrote this version, using span:
def groupBy[A](as: List[A])(p: (A, A) => Boolean): List[List[A]] =
as match {
case x :: xs =>
val (ys, zs) = xs.span(p(x, _))
(x :: ys) :: groupBy(zs)(p)
case _ => Nil
}
This is essentially similar to ghik's solution, but using pattern matching instead of isEmpty and head.
(An explanation of the name groupBy: there is a function of the same name in the Haskell library, which has this exact same behaviour.)
Little more general solution:
def partitionBy[A](list: List[A])(groupingFunc: A => Any): List[List[A]] =
if (list.nonEmpty) {
val firstGroupingValue = groupingFunc(list.head)
val (group, rest) = list.span(groupingFunc(_) == firstGroupingValue)
group :: partitionBy(rest)(groupingFunc)
} else Nil
Example usage:
scala> partitionBy(List((1,2),(3,2),(5,2),(1,1),(2,1),(3,2),(5,2)))(_._2)
res14: List[List[(Int, Int)]] = List(List((1,2), (3,2), (5,2)), List((1,1), (2,1)), List((3,2), (5,2)))
You could try foldRight
val result2 = l.foldRight(List[List[(Int, Int)]]()) {
(x, acc) =>
if (acc.isEmpty) {
List(x) :: Nil
} else if (acc.head.head._2 == x._2) {
(x :: acc.head) :: acc.tail
} else {
List(x) :: acc
}
}
Your problem is too specific for there to exist a general function solving it, so we'll have to write a function of our own.
The standard strategy to implementing functional algorithms is Divide and conquer, which basically means extracting the smallest part of the problem and then gradually building up the algorithm over it.
Implementation
Okay, the obviously smallest thing we'll need is to test two items for contiguity:
def testContiguity( a : (Int, Int), b : (Int, Int) ) : Boolean
= a._2 == b._2
Then we'll need some function arranging the lists using the two-item comparison function. Would be nice if the standard library had it, but it doesn't, so we define our own:
def arrange
[ A ]
( list : List[ A ] )
( f : (A, A) => Boolean )
: List[ List[ A ] ]
= list match {
case a :: b :: tail =>
if( f(a, b) ) putInFirstGroup( a, arrange( b :: tail )( f ) )
else putInNewGroup( a, arrange( b :: tail )( f ) )
case a :: Nil =>
putInNewGroup( a, Nil )
case Nil =>
Nil
}
Okay, you can see that the above implementation relies on two other functions, let's define them too:
def putInFirstGroup
[ A ]
( item : A, groups : List[ List[ A ] ] )
: List[ List[ A ] ]
= groups match {
case group :: tail =>
(item :: group) :: tail
case Nil =>
(item :: Nil) :: Nil
}
def putInNewGroup
[ A ]
( item : A, groups : List[ List[ A ] ] )
: List[ List[ A ] ]
= (item :: Nil) :: groups
That's it!
Usage
scala> arrange( List( (1,2), (3,2), (4, 1), (6,2) ) )( testContiguity )
res2: List[List[(Int, Int)]] = List(List((1,2), (3,2)), List((4,1)), List((6,2)))
You can now see that we've created a pretty flexible and general solution, working on lists of items of any type and allowing you to use any other testing function to arrange them. Also we've heavily utilized division of a complex algorithm to small easily understandable parts to solve this.
Related
I have a list of elements in Scala and I am looking for a way to split the list when a duplicate is found.
For example: List(x,y,z,e,r,y,g,a) would be converted to List(List(x,y,z,e,r),List(y,g,a))
or List(x,y,z,x,y,z) to List(x,y,z), List(x,y,z)
and List(x,y,z,y,g,x) to List(x,y,z), List(y,g,x)
Is there a more efficient way than iterating and and cheking for every element separately?
Quick and dirty O(n) using O(n) additional memory:
import scala.collection.mutable.HashSet
import scala.collection.mutable.ListBuffer
val list = List("x", "y", "z", "e", "r", "y", "g", "a", "x", "m", "z")
var result = new ListBuffer[ListBuffer[String]]()
var partition = new ListBuffer[String]()
list.foreach { i =>
if (partition.contains(i)) {
result += partition
partition = new ListBuffer[String]()
}
partition += i
}
if (partition.nonEmpty) {
result += partition
}
result
ListBuffer(ListBuffer(x, y, z, e, r), ListBuffer(y, g, a, x, m, z))
This solution comes with a few caveats:
I'm not making a claim as to 'performance', though I think it's better than O(n^2), which is the brute-force.
This is assuming you are splitting when you find a duplicate, where 'duplicate' means 'something that exists in the previous split'. I cheat a little by only checking the last segment. The reason is that I think it clarifies how to use foldLeft a little, which is a natural way to go about this.
Everything here is reversed, but maintains order. This can be easily corrected, but adds an additional O(n) (cumulative) call, and may not actually be needed (depending on what you're doing with it).
Here is the code:
def partition(ls: List[String]): List[ListSet[String]] = {
ls.foldLeft(List(ListSet.empty[String]))((partitionedLists, elem:String) => {
if(partitionedLists.head.contains(elem)) {
ListSet(elem) :: partitionedLists
} else {
(partitionedLists.head + elem) :: partitionedLists.tail
}
})
}
partition(List("x","y","z","e","r","y","g","a"))
// res0: List[scala.collection.immutable.ListSet[String]] = List(ListSet(r, e, z, y, x), ListSet(a, g, y))
I'm using ListSet to get both the benefits of a Set and ordering, which is appropriate to your use case.
foldLeft is a function that takes an accumulator value (in this case the List(ListSet.empty[String])) and modifies it as it moves through the elements of your collection. If we structure that accumulator, as done here, to be a list of segments, by the time we're done it will have all the ordered segments of the original list.
One statement tail-recursive version (but not very efficient because of the contains on the list)
var xs = List('x','y','z','e','r','y','g','a')
def splitAtDuplicates[A](splits: List[List[A]], right: List[A]): List[List[A]] =
if (right.isEmpty)// done
splits.map(_.reverse).reverse
else if (splits.head contains right.head) // need to split here
splitAtDuplicates(List()::splits, right)
else // continue building current sublist
splitAtDuplicates((right.head :: splits.head)::splits.tail, right.tail)
Speed it up with a Set to track what we've seen so far:
def splitAtDuplicatesOptimised[A](seen: Set[A],
splits: List[List[A]],
right: List[A]): List[List[A]] =
if (right.isEmpty)
splits.map(_.reverse).reverse
else if (seen(right.head))
splitAtDuplicatesOptimised(Set(), List() :: splits, right)
else
splitAtDuplicatesOptimised(seen + right.head,
(right.head :: splits.head) :: splits.tail,
right.tail)
You will basically need to iterate with a look-up table. I can provide help with the follwoing immutable and functional tailrec implementation.
import scala.collection.immutable.HashSet
import scala.annotation.tailrec
val list = List("x","y","z","e","r","y","g","a", "x", "m", "z", "ll")
def splitListOnDups[A](list: List[A]): List[List[A]] = {
#tailrec
def _split(list: List[A], cList: List[A], hashSet: HashSet[A], lists: List[List[A]]): List[List[A]] = {
list match {
case a :: Nil if hashSet.contains(a) => List(a) +: (cList +: lists)
case a :: Nil => (a +: cList) +: lists
case a :: tail if hashSet.contains(a) => _split(tail, List(a), hashSet, cList +: lists)
case a :: tail => _split(tail, a +: cList, hashSet + a, lists)
}
}
_split(list, List[A](), HashSet[A](), List[List[A]]()).reverse.map(_.reverse)
}
def splitListOnDups2[A](list: List[A]): List[List[A]] = {
#tailrec
def _split(list: List[A], cList: List[A], hashSet: HashSet[A], lists: List[List[A]]): List[List[A]] = {
list match {
case a :: Nil if hashSet.contains(a) => List(a) +: (cList +: lists)
case a :: Nil => (a +: cList) +: lists
case a :: tail if hashSet.contains(a) => _split(tail, List(a), HashSet[A](), cList +: lists)
case a :: tail => _split(tail, a +: cList, hashSet + a, lists)
}
}
_split(list, List[A](), HashSet[A](), List[List[A]]()).reverse.map(_.reverse)
}
splitListOnDups(list)
// List[List[String]] = List(List(x, y, z, e, r), List(y, g, a), List(x, m), List(z, ll))
splitListOnDups2(list)
// List[List[String]] = List(List(x, y, z, e, r), List(y, g, a, x, m, z, ll))
I am trying to append the concatenation of the head to the concatenation of the tail and the other list. However, I have an error: recursive method concat needs result type.
case z :: zs => z :: concat(zs, ys))
The error is at :: when I do z :: concat(zs, ys).
The full code:
def concat[T](xs: List[T], ys: List[T]) = xs match {
case List() => ys
case z :: zs => z :: concat(zs, ys)
}
var list_1 = List(1,2)
var list_2 = List(2,3)
var list_3 = concat(list_1, list_2)
Here's a sentence quoted from the documentation:
For recursive methods, the compiler is not able to infer a result type
Since you're defining your function recursively, you'll need to provide the result type of the function:
def concat[T](xs: List[T], ys: List[T]): List[T] = xs match {
case List() => ys
case z :: zs => z :: concat(zs, ys)
}
Have two Lists :
val list1 : List[List[(String, String)]] = List(List("1" -> "a" , "1" -> "b"))
//> list1 : List[List[(String, String)]] = List(List((1,a), (1,b)))
val list2 : List[List[(String, String)]] = List(List("2" -> "a" , "2" -> "b"))
//> list2 : List[List[(String, String)]] = List(List((2,a), (2,b)))
//Expecting
val toConvert = List(List(Map("1" -> "a" , "2" -> "b"), Map("1" -> "b" , "2" -> "a")))
Attempting to convert these lists to type :
List[List[scala.collection.immutable.Map[String,String]]] = Lis
//| t(List(Map(1 -> a, 2 -> b), Map(1 -> b, 2 -> a)))
Using this code :
val ll = list1.zip(list2).map(m => List(m._1.toMap , m._2.toMap))
But Map entries are missing :
List[List[scala.collection.immutable.Map[String,String]]] = List(List(
//| Map(1 -> b), Map(2 -> b)))
How to convert list1,list2 to type List[List[scala.collection.immutable.Map[String,String]]] which includes values : (List(Map(1 -> a, 2 -> b), Map(1 -> b, 2 -> a))) ?
Update :
Logic of : Map("1" -> "a" , "2" -> "b")
Combine each List using the zip functions :
val p1 : List[(List[(String, String)], List[(String, String)])] = list1.zip(list2);
Convert each element of thenewly created List to a Map and then add to a newly created List :
val p2 = p1.map(m => List(m._1.toMap , m._2.toMap))
How to convert list1 and list2 to type List[List[Map[...
Starting from the type List[List[(String, String)]] we would like to get to the type List[List[Map[String, String]]].
Inner Type
The inner type we want is Map[String, String]. As I asked in the comments, I don't fully understand the expected logic to construct this Map so I am assuming you want to create a Map[String, String] from a list of tuples List[(String, String)].
When creating the Map using .toMap, the key-value elements with the same key will get overwritten as we can see clearly from the b += x in its implementation:
def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = {
val b = immutable.Map.newBuilder[T, U]
for (x <- self)
b += x
b.result()
}
(source TraversableOnce.scala)
So the logic we use to create List[(String, String)] will determine the generated Map[String, String].
Outer List of List
Combine each List using the zip functions
type A = List[(String, String)]
val list12: List[(A, A)] = list1 zip list2
Zipping list1 and list2 gives us a list of tuples but the type we want is actually a list of list of tuple: List[List[(A, A)]]
In your example you are getting that type mapping to a list m => List(...). That is the key part. Let me split that in 2 parts to make it clearer:
list12.map(m => List(m._1 , m._2)).map(_.map(_.toMap))
Let's extract that into a separate function:
def keyPart: ((A, A)) => List[A] = { case (l1, l2) => List(l1, l2) }
val resultNotExpected = list12.map(keyPart).map(_.map(_.toMap))
The result is of course the same of the one in your question:
resultNotExpected == List(List(Map("1" -> "b"), Map("2" -> "b")))
The "keyPart"
In your question you mentioned the expected result as:
List(List(Map("1" -> "a", "2" -> "b"), Map("1" -> "b", "2" -> "a")))
I still don't understand the logic you have in mind for the now extracted keyPart function so I would give you mine... over-complex of course:
val resultExpected = list12.map { case (l1, l2) => List(l1, l2) }
.map(_.map(_.zipWithIndex)).map(_.zipWithIndex)
.map(_.map { case (l, i) => l.map { case ((k, v), j) => (k, v, i, j) } }).map(_.flatten)
.map(_.groupBy { case (k, v, i, j) => i == j }).map(_.values.toList)
.map(_.map(_.map { case (k, v, _, _) => k -> v }.sorted).sortBy(_.head._2))
.map(_.map(_.toMap))
scala> resultExpected == List(List(Map("1" -> "a", "2" -> "b"), Map("1" -> "b", "2" -> "a")))
Boolean = true
Perhaps you know better the logic to be implemented in keyPart.
def combinations(list: List[(Char, Int)]) : List[List[(Char,Int)]]= {
val t = List.range(0, 1)
list match {
case List() => List()
case (c,i) :: xs => val res = for {
o <- List.range(1, i + 1)
} yield List((c, o)) :: combinations(xs)
List()
}
}
I have the following function which won't compile if I try to return res instead of List(). It's a type mismatch of List(List(List[(Char,Int)]]] However this code:
List(('a',10)) :: combinations(List())
compiles perfectly and as expected. Why is it that inside the function it won't compile? Isn't it the exact same thing? How could I tackle this?
Your for-comprehension yields an element of type List[List[(Char,Int)]].
For-comprehensions will generate lists of the elements they're yielding, so in this case that will be a List[List[List[(Char,Int)]]].
I'm not entirely sure what you're trying to achieve, but I think it'll be something like this:
def combinations(list: List[(Char, Int)]) : List[List[(Char,Int)]]= {
val t = List.range(0, 1)
list match {
case Nil => List()
case (c,i) :: xs =>
(for {
o <- List.range(1, i + 1)
} yield (c, o)) :: combinations(xs)
}
}
The for-comprehension generates a List[(Char, Int)] which is added at the head of the list generated by your combinations method.
How do I merge 2 lists in such a way that the resulting list contains the elements of 2 lists in alternating fashion in Scala.
Input:
val list1 = List("Mary", "a", "lamb")
val list2 = List("had", "little")
Output:
List("Mary", "had", "a", "little", "lamb")
What you're looking for is usually called "intersperse" or "intercalate" and there are a few ways to do it:
def intersperse[A](a : List[A], b : List[A]): List[A] = a match {
case first :: rest => first :: intersperse(b, rest)
case _ => b
}
You can also use scalaz
import scalaz._
import Scalaz._
val lst1 = ...
val lst2 = ...
lst1 intercalate lst2
Edit: You can also do the following:
lst1.zipAll(lst2,"","") flatMap { case (a, b) => Seq(a, b) }
Come to think of it, I believe the last solution is my favorite since it's most concise while still being clear. If you're already using Scalaz, I'd use the second solution. The first is also very readable however.
And just to make this answer more complete, adding #Travis Brown's solution that is generic:
list1.map(List(_)).zipAll(list2.map(List(_)), Nil, Nil).flatMap(Function.tupled(_ ::: _))
val list1 = List("Mary", "a", "lamb")
val list2 = List("had", "little")
def merge1(list1: List[String], list2: List[String]): List[String] = {
if (list1.isEmpty) list2
else list1.head :: merge(list2, list1.tail)
}
def merge2(list1: List[String], list2: List[String]): List[String] = list1 match {
case List() => list2
case head :: tail => head :: merge(list2, tail)
}
merge1(list1, list2)
merge2(list1, list2)
//> List[String] = List(Mary, had, a, little, lamb)
list1.zipAll(list2,"","").flatMap(_.productIterator.toList).filter(_ != "")
You could do something like this:
def alternate[A]( a: List[A], b: List[A] ): List[A] = {
def first( a: List[A], b: List[A] ): List[A] = a match {
case Nil => Nil
case x :: xs => x :: second( xs, b )
}
def second( a: List[A], b: List[A] ): List[A] = b match {
case Nil => Nil
case y :: ys => y :: first( a, ys )
}
first( a, b )
}