Idiomatic scala solution to combining sequences - scala

Imagine a function combineSequences: (seqs: Set[Seq[Int]])Set[Seq[Int]] that combines sequences when the last item of first sequence matches the first item of the second sequence. For example, if you have the following sequences:
(1, 2)
(2, 3)
(5, 6, 7, 8)
(8, 9, 10)
(3, 4, 10)
The result of combineSequences would be:
(5, 6, 7, 8, 8, 9, 10)
(1, 2, 2, 3, 3, 4, 10)
Because sequences 1, 2, and 5 combine together. If multiple sequences could combine to create a different result, the decisions is arbitrary. For example, if we have the sequences:
(1, 2)
(2, 3)
(2, 4)
There are two correct answers. Either:
(1, 2, 2, 3)
(2, 4)
Or:
(1, 2, 2, 4)
(2, 3)
I can only think of a very imperative and fairly opaque implementation. I'm wondering if anyone has a solution that would be more idiomatic scala. I've run into related problems a few times now.

Certainly not the most optimized solution but I've gone for readability.
def combineSequences[T]( seqs: Set[Seq[T]] ): Set[Seq[T]] = {
if ( seqs.isEmpty ) seqs
else {
val (seq1, otherSeqs) = (seqs.head, seqs.tail)
otherSeqs.find(_.headOption == seq1.lastOption) match {
case Some( seq2 ) => combineSequences( otherSeqs - seq2 + (seq1 ++ seq2) )
case None =>
otherSeqs.find(_.lastOption == seq1.headOption) match {
case Some( seq2 ) => combineSequences( otherSeqs - seq2 + (seq2 ++ seq1) )
case None => combineSequences( otherSeqs ) + seq1
}
}
}
}
REPL test:
scala> val seqs = Set(Seq(1, 2), Seq(2, 3), Seq(5, 6, 7, 8), Seq(8, 9, 10), Seq(3, 4, 10))
seqs: scala.collection.immutable.Set[Seq[Int]] = Set(List(1, 2), List(2, 3), List(8, 9, 10), List(5, 6, 7, 8), List(3, 4, 10))
scala> combineSequences( seqs )
res10: Set[Seq[Int]] = Set(List(1, 2, 2, 3, 3, 4, 10), List(5, 6, 7, 8, 8, 9, 10))
scala> val seqs = Set(Seq(1, 2), Seq(2, 3, 100), Seq(5, 6, 7, 8), Seq(8, 9, 10), Seq(100, 4, 10))
seqs: scala.collection.immutable.Set[Seq[Int]] = Set(List(100, 4, 10), List(1, 2), List(8, 9, 10), List(2, 3, 100), List(5, 6, 7, 8))
scala> combineSequences( seqs )
res11: Set[Seq[Int]] = Set(List(5, 6, 7, 8, 8, 9, 10), List(1, 2, 2, 3, 100, 100, 4, 10))

Related

Scala's List unpacking

So my problem is simple. I have come across this many times, and my brain is unable to find a solution.
How can I unpack a list into another list, for an indefinite amount of variables?
Here is what I mean.
val list1 = List(List(7, 4), List(7, 6))
val list2 = List(List(1), List(5), List(8))
val desired_list1 = List(List(1, 7, 4), List(5, 7, 4), List(8, 7, 4))
val desired_list2 = List(List(1, 7, 6), List(5, 7, 6), List(8, 7, 6))
//** The desired_list1 and 2 must be a List[List[Int]] it cannot be List[List[Any]]
//Here's my attempt, which oddly enough completely ignores all elements of list1(0) which are not the first(7).
val attempt = list2.map(i => i +: list1(0)).map(j => j.collect{ case k:Int => k; case l # a :: b => a}).map(m => m.map{ case i:Any => i.toString.toInt})
//The result is
attempt: List[List[Int]] = List(List(1, 7), List(5, 7), List(8, 7))
//while it should be:
val desired_list1 = List(List(1, 7, 4), List(5, 7, 4), List(8, 7, 4))
I need a way to unpack that is not manual, please do not tell me to do this:
val attempt = list2.map(k => k +: list1(0)).map{ case (k, List(x, y)) => (k, x, y)}
Basically, list1 could have any number of elements. e.g
val list1 = List(List(99, 83, 2, 3, 4), List(99, 83, 2, 5 7))
However, these numbers are never repeated, so I guess it could be a set as well. But I don't know much about sets or if it'd help in any way.
This seems to be what you want:
val lists: List[List[Int]] = List(List(7, 4), List(7, 6))
val prefixes: List[List[Int]] = List(List(1), List(5), List(8))
val res: List[List[Int]] = for{
prefix <- prefixes.flatten
rest <- lists
} yield prefix :: res
// res: List[List[Int]] = List(List(1, 7, 4), List(1, 7, 6), List(5, 7, 4), List(5, 7, 6), List(8, 7, 4), List(8, 7, 6))
Unless, you actually want a list that contains your desired_list1 and desired_list2 together, in which case you need:
val res3 = lists.map{ rest =>
prefixes.flatten.map{prefix =>
prefix :: rest
}
}
/// res3: List[List[List[Int]]] =
// List(
// List(
// List(1, 7, 4), List(5, 7, 4), List(8, 7, 4)
// ),
// List(
// List(1, 7, 6), List(5, 7, 6), List(8, 7, 6)
// )
// )
I am not sure exactly why you would want that but try this out:
val list1 = List(List(7, 4), List(7, 6))
val list2 = List(1, 5, 8)
list2.flatMap(element=> list1.map(innerList=> element:: innerList))
output: List[List[Int]] = List(List(1, 7, 4), List(1, 7, 6), List(5, 7, 4), List(5, 7, 6), List(8, 7, 4), List(8, 7, 6))

Finding all slices delimited by two elements in Scala `List`

In Scala, what would be the right way of selecting elements of a list based on the position of two elements? Suppose I have the list below and I would like to select all the elements between 2 and 7, including them (note: not greater than/smaller than, but the elements that come after 2 and before 7 in the list):
scala> val l = List(1, 14, 2, 17, 35, 9, 12, 7, 9, 40)
l: List[Int] = List(1, 14, 2, 17, 35, 9, 12, 7, 9, 40)
scala> def someMethod(l: List[Int], from: Int, to: Int) : List[Int] = {
| // some code here
| }
someMethod: (l: List[Int], from: Int, to: Int)List[Int]
scala> someMethod(l, 2, 7)
res0: List[Int] = List(2, 17, 35, 9, 12, 7)
Expected output:
For lists that don't contain 2 and/or 7: an empty list
Input: (1, 2, 2, 2, 3, 4, 7, 8); Output: (2, 2, 2, 3, 4, 7)
Input: (1, 2, 3, 4, 7, 7, 7, 8); Output: (2, 3, 4, 7)
Input: (1, 2, 3, 4, 7, 1, 2, 3, 5, 7, 8); Output: ((2, 3, 4, 7), (2, 3, 5, 7))
Too bad that the regex-engines work only with strings, not with general lists - would be really nice if you could find all matches for something like L.*?R with two arbitrary delimiters L and R. Since it doesn't work with regex, you have to build a little automaton yourself. Here is one way to do it:
#annotation.tailrec
def findDelimitedSlices[A](
xs: List[A],
l: A,
r: A,
revAcc: List[List[A]] = Nil
): List[List[A]] = {
xs match {
case h :: t => if (h == l) {
val idx = xs.indexOf(r)
if (idx >= 0) {
val (s, rest) = xs.splitAt(idx + 1)
findDelimitedSlices(rest, l, r, s :: revAcc)
} else {
revAcc.reverse
}
} else {
findDelimitedSlices(t, l, r, revAcc)
}
case Nil => revAcc.reverse
}
}
Input:
for (example <- List(
List(1, 2, 2, 2, 3, 4, 7, 8),
List(1, 2, 3, 4, 7, 7, 7, 8),
List(1, 2, 3, 4, 7, 1, 2, 3, 5, 7, 8)
)) {
println(findDelimitedSlices(example, 2, 7))
}
Output:
List(List(2, 2, 2, 3, 4, 7))
List(List(2, 3, 4, 7))
List(List(2, 3, 4, 7), List(2, 3, 5, 7))
You're looking for slice:
# l.slice(2, 7)
res1: List[Int] = List(2, 17, 35, 9, 12)
# l.slice(2, 8)
res2: List[Int] = List(2, 17, 35, 9, 12, 7)

How to zip but not in pair?

Let a
Seq( Seq(1, 2, 5) , Seq(3, 4, 5) , Seq(2, 1, 0) )
I'd want to get :
Seq( Seq(1, 3, 2) , Seq(2, 4, 1) , Seq(5, 5, 0) )
For the moment I wrote this :
points.reduce((point_1, point_2) => point_1.zip(point_2))
With : points the Seq[Seq[Double]] and point_1 and point_2 the Seq[Double].
It returns an error because Scala's interpreter seems to try to make a pair with a Seq[(Double, Double)] and Double (I think). In my example, it tries to make a pair with Seq( (1, 3) ) and 2. I can be wrong but it's my interpretation of the problem for now.
Well, how to solve this bug ? I feel like I need to use flatten, no ?
The standard library can do that for you.
Seq( Seq(1, 2, 5) , Seq(3, 4, 5) , Seq(2, 1, 0) ).transpose
//res0: Seq[Seq[Int]] = List(List(1, 3, 2), List(2, 4, 1), List(5, 5, 0))
update
If you're intent on doing it yourself, in a functional fashion, here's one way.
val ssi: Seq[Seq[Int]] = Seq(Seq(1, 2, 5), Seq(3, 4, 5), Seq(2, 1, 0))
ssi.zipWithIndex.map{case (s,x) =>
s.indices.map(ssi(_)(x))
}
If you want to do it using map and fold,
val seqInput = Seq( Seq(1, 2, 5) , Seq(3, 4, 5) , Seq(2, 1, 0) )
// seqInput: Seq[Seq[Int]] = List(List(1, 2, 5), List(3, 4, 5), List(2, 1, 0))
val seqOutput = seqInput
.map(s => Seq(s))
.fold(
seqInput.head.map(_ => Seq.empty[Int])
)({
case (acc, seq) => acc.zipWithIndex.map({ case (accSeq, i) => accSeq :+ seq.head(i) })
})
// seqOutput: Seq[Seq[Int]] = List(List(1, 3, 2), List(2, 4, 1), List(5, 5, 0))

How to summarize lists in Scala?

to All!
i have this data structure:
List[List[String]]
all List[String] are the same size, it looks like:
List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
i need to summarize columns in this lists like this:
val result = List(1 + 4 + 7, 2 + 5 + 8, 3 + 6 + 9)
with such algorithm
res[0] = list1[0] + list2[0] + list3[0]
res[1] = list1[1] + list2[2] + list3[3]
...
help, please!
You're looking for the transpose method:
scala> val in = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
in: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
scala> in.transpose
res0: List[List[Int]] = List(List(1, 4, 7), List(2, 5, 8), List(3, 6, 9))
scala> in.transpose.map(_.sum)
res1: List[Int] = List(12, 15, 18)

Scala: Any predefined function to iterate over list(0), then list(0, 1), then list(0, 1, 2), etc.?

I want to iterate over a scala list in an incremental way, i.e. the first pass should yield the head, the second the first 2 elements, the next the first 3, etc...
I can code this myself as a recursive function, but does a pre-existing function exist for this in the standard library?
You can use the .inits method to get there, albeit there may be performance issues for a large list (I haven't played around with making this lazy):
scala> val data = List(0,1,2,3,4)
data: List[Int] = List(0, 1, 2, 3, 4)
scala> data.inits.toList.reverse.flatten
res2: List[Int] = List(0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4)
You can use the take like so:
scala> val myList = 1 to 10 toList
myList: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> for(cnt <- myList.indices) yield myList.take(cnt+1)
res1: scala.collection.immutable.IndexedSeq[List[Int]] = Vector(List(1), List(1, 2), List(1, 2, 3), List(1, 2, 3, 4), List(1, 2, 3, 4, 5), List(1, 2, 3, 4, 5, 6), List(1, 2, 3, 4, 5, 6, 7), List(1, 2, 3, 4, 5, 6, 7, 8), List(1, 2, 3, 4, 5, 6, 7, 8, 9), List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
OK, since I've whined enough, here's an iterator version that tries reasonably hard to not waste space or compute more than is needed at at one point:
class stini[A](xs: List[A]) extends Iterator[List[A]] {
var ys: List[A] = Nil
var remaining = xs
def hasNext = remaining.nonEmpty
def next = {
val e = remaining.head
remaining = remaining.tail
ys = e :: ys
ys.reverse
}
}
val it = new stini(List(1, 2, 3, 4))
it.toList
//> List[List[Int]] =
// List(List(1), List(1, 2), List(1, 2, 3), List(1, 2, 3, 4))
Try: for((x, i) <- l.view.zipWithIndex) println(l.take(i + 1))
if you need something side-effected (I just did println to give you an example)