I have this function that basically combines Lists of Lists, but you can ignore that, for that is not the issue:
def combinationList[T](ls:List[List[List[T]]]) : List[List[List[T]]] = ls match {
case head :: Nil => for(x <- head) yield List(x)
case head :: tail :: Nil =>
for {
hl <- head
tl <- tail
if tl.forall(te => !hl.contains(te))
} yield List(hl, tl)
}
This works when I have a List with two sub-lists but it fails to match when I have more than two. I obviously need to use this function recursively to make a more general case, and I've tried to add the following case at the end of the function:
case _ => combinationList(combinationList(ls.tail):::List(ls.head))
The logic here is that, imagine we have a List with 3 sub lists, like so:
List ( List(1), List(2), List(3) )
When I first call the fucntion, it will only match in the last case I mentioned, so, I will call the function with the list's tail (List(2), List(3)), and, in that call, it will match for the second case (head::tail::Nil), then, when it returns, it should be a single List and when added to the list's original head (List(1)::List(2+3)) it should match on the second condition. The issue here is that the return is not a single list (List(2+3)) but rather yet another combination of lists (List(2), List(3)) so it will obviously recursive forever. I've tried to modify the case like this:
combinationList(List(combinationList(ls.tail)):::List(ls.head))
But it gives me an error ("The expression of type List[List[List[Any]]] doesn't conform to type List[List[List[T]]]"
Any ideas? Thanks.
[EDIT:] This function's purpose is to combine lists in such manner:
simplified input: List( List(1,2), List(3), List(4))
simplified output: List( List(1,3,4), List(2,3,4))
real input: List(
List(List(1), List(3), List(4)),
List(List(2), List(3), List(4))
)
real expected output: List(
List(List(1), List(2)),
List(List(1), List(3)),
List(List(1), List(4)),
List(List(3), List(2)),
List(List(3), List(4)),
List(List(4), List(2)),
List(List(4), List(3))
)
With the real input, the function is able to return the expected output, it only fails when another sub list is added, like so:
input: List(
List(List(1), List(2)),
List(List(3), List(4)),
List(List(5))
)
The expected output here, would be:
expected output: List(
List(List(1), List(3), List(5)),
List(List(1), List(4), List(5)),
List(List(2), List(3), List(5)),
List(List(2), List(4), List(5))
)
So, the order of the combinations don't really matter much, I can head:::tail or tail:::head, it's the same thing.
I still don't know why you want three levels of nesting. However, the following generic method works with all your examples, both with integers and with lists:
def combinationList[T](ls: List[List[T]]): List[List[T]] = {
def recHelper(remainingLs: List[List[T]], blacklist: Set[T]): List[List[T]] = {
remainingLs match {
case Nil => List(Nil)
case h :: t => for {
x <- h.filterNot(blacklist.contains)
xs <- recHelper(t, blacklist + x)
} yield x :: xs
}
}
recHelper(ls, Set.empty)
}
This implementation doesn't filter out any combinations with ! .contains, instead it maintains a set of blacklisted elements, and doesn't generate the invalid combinations in the first place.
Some examples:
def showExample[T](input: List[List[T]]): Unit = {
println("=" * 60)
println(input)
println("-" * 60)
for (c <- combinationList(input)) {
println(" " + c)
}
}
showExample(List(List(1, 2), List(3), List(4)))
showExample(List(List(1, 2, 3), List(2, 3, 4), List(2, 3, 5)))
showExample(List(
List(List(1), List(3), List(4)),
List(List(2), List(3), List(4))
))
showExample(List(
List(List(1), List(2)),
List(List(3), List(4)),
List(List(5))
))
Output:
============================================================
List(List(1, 2), List(3), List(4))
------------------------------------------------------------
List(1, 3, 4)
List(2, 3, 4)
============================================================
List(List(1, 2, 3), List(2, 3, 4), List(2, 3, 5))
------------------------------------------------------------
List(1, 2, 3)
List(1, 2, 5)
List(1, 3, 2)
List(1, 3, 5)
List(1, 4, 2)
List(1, 4, 3)
List(1, 4, 5)
List(2, 3, 5)
List(2, 4, 3)
List(2, 4, 5)
List(3, 2, 5)
List(3, 4, 2)
List(3, 4, 5)
============================================================
List(List(List(1), List(3), List(4)), List(List(2), List(3), List(4)))
------------------------------------------------------------
List(List(1), List(2))
List(List(1), List(3))
List(List(1), List(4))
List(List(3), List(2))
List(List(3), List(4))
List(List(4), List(2))
List(List(4), List(3))
============================================================
List(List(List(1), List(2)), List(List(3), List(4)), List(List(5)))
------------------------------------------------------------
List(List(1), List(3), List(5))
List(List(1), List(4), List(5))
List(List(2), List(3), List(5))
List(List(2), List(4), List(5))
Some hints:
The more generic versions of the code are often easier to write, because if there are more constraints, there are fewer possibilities to do something wrong. In this case, the triple nesting seems unnecessary.
The base case is the empty list, not the list with one element
The recursion must work with lists of arbitrary length, but x :: y :: Nil matches only lists of length two.
Related
What would be short functional way to split list
List(1, 2, 3, 4, 5) into List((1,2), (2, 3), (3, 4), (4, 5))
(assuming you don't care if you nested pairs are Lists and not Tuples)
Scala collections have a sliding window function:
# val lazyWindow = List(1, 2, 3, 4, 5).sliding(2)
lazyWindow: Iterator[List[Int]] = non-empty iterator
To realize the collection:
# lazyWindow.toList
res1: List[List[Int]] = List(List(1, 2), List(2, 3), List(3, 4), List(4, 5))
You can even do more "funcy" windows, like of length 3 but with step 2:
# List(1, 2, 3, 4, 5).sliding(3,2).toList
res2: List[List[Int]] = List(List(1, 2, 3), List(3, 4, 5))
You can zip the list with its tail:
val list = List(1, 2, 3, 4, 5)
// list: List[Int] = List(1, 2, 3, 4, 5)
list zip list.tail
// res6: List[(Int, Int)] = List((1,2), (2,3), (3,4), (4,5))
I have always been a big fan of pattern matching. So you could also do:
val list = List(1, 2, 3, 4, 5, 6)
def splitList(list: List[Int], result: List[(Int, Int)] = List()): List[(Int, Int)] = {
list match {
case Nil => result
case x :: Nil => result
case x1 :: x2 :: ls => splitList(x2 :: ls, result.:+(x1, x2))
}
}
splitList(list)
//List((1,2), (2,3), (3,4), (4,5), (5,6))
Given something like
val list: List[List[Int]] = List(List(5), List(1), List(2), List(3), List(4), List(5, 1), List(5, 2), List(5, 3))
How would one get the list of each sum inner list and return them as a list, i.e.: List(5,1,2,3,4,6,7,8)
I created a function sum which takes a list and returns the sum. I'm unsure how to apply it to each element in this list.. (New to Scala)
You can use a map to do this
list.map(_.sum)
or
list.map(innerList => sum(innerList))
Instead of the custom sum function you could use standard lib sum function
scala> val lists = List(List(1, 2), List(3, 4))
lists: List[List[Int]] = List(List(1, 2), List(3, 4))
scala> lists.map(_.sum)
res11: List[Int] = List(3, 7)
If I have unknown number of Set[Int] (or List[Int]) as an input and want to combine
i don't know size of input List[Int] for which I need to produce these tuples as a final result, what's the best way to achieve this? My code looks like below.
Ok. Since combine(xs) yields a List[List[Any]] and you have a :: combine(xs) you just insert a into the the List of all combinations. You want to combine a with each element of the possible combinations. That lead me to this solution.
You can also generalize it to lists:List[List[T]] because when you combine from lists:List[List[Int]] you will get a List[List[Int]].
def combine[T](lists: List[List[T]]): List[List[T]] = lists match {
case Nil => lists
case x :: Nil => for(a <- x) yield List(a) //needed extra case because if comb(xs) is Nil in the for loop, it won't yield anything
case x :: xs => {
val comb = combine(xs) //since all further combinations are constant, you should keep it in a val
for{
a <- x
b <- comb
} yield a :: b
}
}
Tests:
val first = List(7, 3, 1)
val second = List(2, 8)
val third = List("a","b")
combine(List(first, second))
//yields List(List(7, 2), List(7, 8), List(3, 2), List(3, 8), List(1, 2), List(1, 8))
combine(List(first, second, third))
//yields List(List(7, 2, a), List(7, 2, b), List(7, 8, a), List(7, 8, b), List(3, 2, a), List(3, 2, b), List(3, 8, a), List(3, 8, b), List(1, 2, a), List(1, 2, b), List(1, 8, a), List(1, 8, b))
I think you can also generalize this to work with other collections than List, but then you can't use pattern-match this easily and you have to work via iterators.
There is a List[List[Int]] of prime factors for integers 2..12:
List(List(2), List(3), List(2, 2), List(5), List(3, 2),
List(7), List(2, 2, 2), List(3, 3), List(5, 2),
List(11), List(3, 2, 2))
It needs to be flattened so that the resulting data structure contains only the longest sequence (greatest power) of each prime:
List(2,2,2,3,3,5,7,11)
For example, leaving out all but the greatest power of two:
List(List(2), List(3), List(2, 2), List(5), List(3, 2),
List(7), List(2, 2, 2), List(3, 3), List(5, 2),
List(11), List(3, 2, 2))
Within the initial list sub-lists of primes are always sorted in the descending order.
Struggling to find an elegant, preferably ≤O(n) solution.
My solution is far from ideal:
xs.flatMap(l=> l.groupBy(x=>x)).map(x=>(x._1,x._2.length)).
groupBy(_._1).mapValues(_.maxBy(_._2)).values.
map(x=>List.fill(x._2) (x._1)).flatten
This is a fair bit shorter than what you have; it's close enough conceptually that I expect you can figure it out:
xs.flatMap(_.groupBy(x=>x)).groupBy(_._1).
flatMap(_._2.sortBy(- _._2.length).head._2).toSeq.sorted
After some analysis the problem boils down to a simple merge of two sorted lists, but with a slight twist - it must add duplicate elements only once:
merge(List(5,3,3,2),List(7,5,3,2,2)
Must produce:
List(7,5,3,3,2,2)
Once there is such wonderful merge function the list of lists can be simply reduced from left to right.
Solution
def merge (xs:List[Int],ys:List[Int]):List[Int] = (xs,ys) match{
case (Nil,_) => ys
case (_,Nil) => xs
case (x::xxs,y::yys) => if (x==y) x::merge(xxs,yys)
else if (x>y) x::merge(xxs,ys)
else y::merge(xs,yys)
}
// note the simplicity of application
ll reduce merge
Tail recursive version of merge - avoiding stack overflow on long lists :
def merge (xs:List[Int],ys:List[Int]) = {
def m (rs:List[Int],xs:List[Int],ys:List[Int]):List[Int] = (xs,ys) match {
case (Nil,_) => ys reverse_:::rs
case (_,Nil) => xs reverse_:::rs
case (x::xxs,y::yys) => if (x==y) m(x::rs,xxs,yys)
else if (x>y) m(x::rs,xxs,ys)
else m(y::rs,xs,yys)
}
m(Nil,xs,ys).reverse
}
Faster imperative version of merge:
def merge (x:List[Int],y:List[Int]) = {
var rs = new scala.collection.mutable.ListBuffer[Int]()
var xs = x
var ys = y
while(!xs.isEmpty && !ys.isEmpty) {
if (xs.head>ys.head) {
rs+=xs.head
xs=xs.tail
} else if(xs.head==ys.head) {
rs+=xs.head
xs=xs.tail
ys=ys.tail
} else {
rs+=ys.head
ys=ys.tail
}
}
rs ++= xs ++= ys toList
}
val ll = List(List(2), List(3), List(2, 2), List(5), List(3, 2),
List(7), List(2, 2, 2), List(3, 3), List(5, 2),
List(11), List(3, 2, 2))
val s = ll.flatten toSet
s.map (n => ll.map (l => (n, l.count (_ == n)))).map (l => (l(0) /: l.tail) ((a, b) => if (a._2 > b._2) a else b))
produces:
scala.collection.immutable.Set[(Int, Int)] = Set((7,1), (11,1), (2,3), (5,1), (3,2))
expanding the factors and sorting them, to generate List (2,2,2,3,3,5,7,11) is left as an exercise.
After going through some examples on the web I realize that there is a way to write an anonymous function without the underscore when only a single arg. Also, I'm experimenting with the "span" method on List, which I never knew existed. Anyway, here is my REPL session:
scala> val nums = List(1, 2, 3, 4, 5)
nums: List[Int] = List(1, 2, 3, 4, 5)
scala> nums span (_ != 3)
res0: (List[Int], List[Int]) = (List(1, 2),List(3, 4, 5))
scala> nums span (3 !=)
res1: (List[Int], List[Int]) = (List(1, 2),List(3, 4, 5))
So far so good. But when I try to use the "less than" operator:
scala> nums span (_ < 3)
res2: (List[Int], List[Int]) = (List(1, 2),List(3, 4, 5))
scala> nums span (3 <)
res3: (List[Int], List[Int]) = (List(),List(1, 2, 3, 4, 5))
Why is this behaving differently?
scala> nums span (_ < 3)
res0: (List[Int], List[Int]) = (List(1, 2),List(3, 4, 5))
scala> nums span (3 >)
res1: (List[Int], List[Int]) = (List(1, 2),List(3, 4, 5))
3 < is a shortcut to 3 < _, which creates a partially applied function from method call.
It's behaving correctly:
scala> nums span (3 < _)
res4: (List[Int], List[Int]) = (List(),List(1, 2, 3, 4, 5))
The predicate is false for the first element of the list, so the first list returned by span is empty.
scala> nums span (3 < _)
res0: (List[Int], List[Int]) = (List(),List(1, 2, 3, 4, 5))
// is equivalent to
scala> (nums takeWhile{3 < _}, nums.dropWhile{3 < _})
res1: (List[Int], List[Int]) = (List(),List(1, 2, 3, 4, 5))
where
the predicate is false already for the first element(1) therefore nums.takeWhile{false} results in the empty list List()
For the second part nothing is dropped because the predicate is false already for the first
element(1) and therefore the nums.dropWhile{false} is the whole list List(1, 2, 3, 4, 5).