Concat sorted generic sequence - scala

I need to concat two generic sequences, I tried to do it like this, but I understand that this is wrong. How should I do it in right way? (I need to get new Seq which will be ordered too)
object Main extends App{
val strings = Seq("f", "d", "a")
val numbers = Seq(1,5,4,2)
val strings2 = Seq("c", "b");
val strings3 = strings2.concat(strings)
println(strings3)
println(numbers)
}
class Seq[T] private(initialElems: T*) {
override def toString: String = initialElems.toString
val elems = initialElems
def concat(a:Seq[T]) = a.elems ++ this.elems
}
object Seq {
def apply[T: Ordering](initialElems: T*): Seq[T] = new Seq(initialElems.sorted:_*)
}

You could create a function which will go through both lists taking heads of both lists and comparing them, then appending appropriate head to the result list. Then it should take the next two heads and repeat until one list is over.
Here's tail-recursive example:
import scala.annotation.tailrec
def merge[A](a: List[A], b: List[A])(implicit ordering: Ordering[A]): List[A] = {
#tailrec
def go(a: List[A], b: List[A], acc: List[A] = Nil): List[A] = {
(a, b) match {
case (ax :: as, bx :: bs) => if(ordering.compare(ax, bx) < 0) go(as, bx :: bs, ax :: acc) else go(ax :: as, bs, bx :: acc)
case (Nil, bs) => acc.reverse ++ bs
case (as, Nil) => acc.reverse ++ as
case _ => acc.reverse
}
}
go(a, b)
}
val strings = List("a", "d", "f")
val strings2 = List("b", "c")
merge(strings, strings2) // List(a,b,c,d,e)
I used List instead of Seq. You should rather not use Seq, which is very general type, but utilize more specific collection types, which suit your task best, like Vector, List, ArraySeq etc.

You can't concat two sorted arrays using ++ keeping order. ++ just stick one sequence to the end of another.
You need to implement something like merge operation from merge sort algorithm and create new Seq from merged elems without sorting.
So, you need to do 3 things:
Implement merge:
def merge(a: Seq[T], b: Seq[T]): YourElemsType[T] = ???
Implement new method for creating Seq instance without sorting in object Seq:
def fromSorted(initialElems: T*): Seq[T] = new Seq(initialElems:_*)
After all, your concat can be implemented as composition merge and fromSorted:
def concat(a:Seq[T]): Seq[T] = Seq.fromSorted(merge(this, a))
Read more about merge sort wiki

Related

How to improve this "update" function?

Suppose I've got case class A(x: Int, s: String) and need to update a List[A] using a Map[Int, String] like that:
def update(as: List[A], map: Map[Int, String]): List[A] = ???
val as = List(A(1, "a"), A(2, "b"), A(3, "c"), A(4, "d"))
val map = Map(2 -> "b1", 4 -> "d1", 5 -> "e", 6 -> "f")
update(as, map) // List(A(1, "a"), A(2, "b1"), A(3, "c"), A(4, "d1"))
I am writing update like that:
def update(as: List[A], map: Map[Int, String]): List[A] = {
#annotation.tailrec
def loop(acc: List[A], rest: List[A], map: Map[Int, String]): List[A] = rest match {
case Nil => acc
case as => as.span(a => !map.contains(a.x)) match {
case (xs, Nil) => xs ++ acc
case (xs, y :: ys) => loop((y.copy(s = map(y.x)) +: xs) ++ acc, ys, map - y.x)
}
}
loop(Nil, as, map).reverse
}
This function works fine but it's suboptimal because it continues iterating over the input list when map is empty. Besides, it looks overcomplicated. How would you suggest improve this update function ?
If you can not make any supposition about the List and the Map. Then the best is to just iterate the former, juts once and in the simplest way possible; that is, using the map function.
list.map { a =>
map
.get(key = a.x)
.fold(ifEmpty = a) { s =>
a.copy(s = s)
}
}
However, if and only if, you can be sure that most of the time:
The List will be big.
The Map will be small.
The keys in the Map are a subset of the values in the List.
And all operations will be closer to the head of the List rather than the tail.
Then, you could use the following approach which should be more efficient in such cases.
def optimizedUpdate(data: List[A], updates: Map[Int, String]): List[A] = {
#annotation.tailrec
def loop(remaining: List[A], map: Map[Int, String], acc: List[A]): List[A] =
if (map.isEmpty) acc reverse_::: remaining
else remaining match {
case a :: as =>
map.get(key = a.x) match {
case None =>
loop(
remaining = as,
map,
a :: acc
)
case Some(s) =>
loop(
remaining = as,
map = map - a.x,
a.copy(s = s) :: acc
)
}
case Nil =>
acc.reverse
}
loop(remaining = data, map = updates, acc = List.empty)
}
However note that the code is not only longer and more difficult to understand.
It is actually more inefficient than the map solution (if the conditions are not meet); this is because the stdlib implementation "cheats" and constructs the List my mutating its tail instead of building it backwards and then reversing it as we did.
In any case, as with any things performance, the only real answer is to benchmark.
But, I would go with the map solution just for clarity or with a mutable approach if you really need speed.
You can see the code running here.
How about
def update(as: List[A], map: Map[Int, String]): List[A] =
as.foldLeft(List.empty[A]) { (agg, elem) =>
val newA = map
.get(elem.x)
.map(a => elem.copy(s = a))
.getOrElse(elem)
newA :: agg
}.reverse

Scala: Append element to list using cons method

While reading Programming in Scala, 3rd Edition, it says
Class List does offer an "append" operation—it's written :+ But this
operation is rarely used, because the time it takes to append to a
list grows linearly with the size of the list, whereas prepending with
:: takes constant time.
If you want to build a list efficiently by appending elements, you can
prepend them and when you're done call reverse.
I am trying to understand, what is Scala idiomatic way of doing this? Is calling List.reverse twice acceptable and efficient vs ListBuffer (since ListBuffer is mutable)?
// Scala Recommended way - but reverse twice?
val alist = List("A", "B")
// cons is O(1)
// This will print (A, B, C)
println(("C" :: alist.reverse).reverse)
// Scala also Recommended: Use ListBuffer
val alb = ListBuffer("A", "B")
alb.append("C")
val clist2 = alb.toList
// This will print (A, B, C)
println(clist2)
// DO NOT do this, its O(n)
val clist3 = alist :+ "C"
// This will print (A, B, C)
println(clist3)
P.S: I'm not referring to code optimization here. Which one is generally recommended and will not received WTH expression.
Another implementation could be Difference Lists (also Prolog-based explanation available - Understanding Difference Lists).
That's how I implement DList in Scala:
abstract class DiffList[A](calculate: List[A] => List[A]) {
def prepend(s: List[A]): DiffList[A]
def append(s: List[A]): DiffList[A]
def result: List[A]
}
final class DiffListImpl[A](listFunc: List[A] => List[A])
extends DiffList[A](listFunc) {
def prepend(s: List[A]): DiffListImpl[A] =
new DiffListImpl[A](listFunc andThen (s ++ _))
def append(s: List[A]): DiffListImpl[A] =
new DiffListImpl[A](listFunc andThen (_ ++ s))
def result: List[A] = listFunc(Nil)
}
And use it:
val l1 = List(1, 2)
val l2 = List(6, 7)
val l3 = List(3, 4, 5)
val dl = new DiffListImpl[Int](Nil)
val result = dl.prepend(l1).prepend(l2).append(l3).result
Result: List(6, 7, 1, 2, 3, 4, 5)

Converting multiple Option[T] to a List[T]

I have several Option[T] that I would like to convert to a List[T]. I'm attempting to achieve this with a for comprehension:
val someOption: Option[(String, String)] = Some(("bla", "woo"))
val anotherOption: Option[String] = None
val result = for {
(a, b) <- someOption
c <- anotherOption
} yield List(a, b, c)
However result in this case becomes of type Option[List[String]] and contains None. How can I instead have result become of type List[String] with a value of List("bla", "woo").
Edit: This is a contrived example, in reality I need to use a and b to instantiate SomeOtherThing(a, b) and have that be a list item.
One way of doing it:
val someOption: Option[(String, String)] = Some(("bla", "woo"))
val anotherOption: Option[String] = None
val someOptionList = for {
(a, b) <- someOption.toList
v <- List(a, b)
} yield v
val result = someOptionList ++ anotherOption.toList
or, to reduce constructions of intermediate collections:
val resultIter = someOption.iterator.flatMap {
case (a, b) => Seq(a,b)
} ++ anotherOption.iterator
val result = resultIter.toList
Hmm, well, the use case is still not absolutely clear to me.
However, here are some thoughts.
The issue with your code is that, in a for comprehension, you always end up with the Collection(like) you start it, so in your case Option.
There is always the possibility to just prepend another Collection to a list with ++:.
So, maybe something like that?:
def addOptionTo(
option: Option[(String, String)],
list: List[SomeOtherThing]
): List[SomeOtherThing] = {
option.map { case (foo, bar) => SomeOtherThing(foo, bar) } ++: list
}

How to create a list with the same element n-times?

How to create a list with the same element n-times ?
Manually implementnation:
scala> def times(n: Int, s: String) =
| (for(i <- 1 to n) yield s).toList
times: (n: Int, s: String)List[String]
scala> times(3, "foo")
res4: List[String] = List(foo, foo, foo)
Is there also a built-in way to do the same ?
See scala.collection.generic.SeqFactory.fill(n:Int)(elem: =>A) that collection data structures, like Seq, Stream, Iterator and so on, extend:
scala> List.fill(3)("foo")
res1: List[String] = List(foo, foo, foo)
WARNING It's not available in Scala 2.7.
(1 to n).map( _ => "foo" )
Works like a charm.
Using tabulate like this,
List.tabulate(3)(_ => "foo")
I have another answer which emulates flatMap I think (found out that this solution returns Unit when applying duplicateN)
implicit class ListGeneric[A](l: List[A]) {
def nDuplicate(x: Int): List[A] = {
def duplicateN(x: Int, tail: List[A]): List[A] = {
l match {
case Nil => Nil
case n :: xs => concatN(x, n) ::: duplicateN(x, xs)
}
def concatN(times: Int, elem: A): List[A] = List.fill(times)(elem)
}
duplicateN(x, l)
}
}
def times(n: Int, ls: List[String]) = ls.flatMap{ List.fill(n)(_) }
but this is rather for a predetermined List and you want to duplicate n times each element

Group values by a key with any Monoid

I would like to write a method mergeKeys that groups the values in an Iterable[(K, V)] by the keys. For example, I could write:
def mergeKeysList[K, V](iter: Iterable[(K, V)]) = {
iter.foldLeft(Map[K, List[V]]().withDefaultValue(List.empty[V])) {
case (map, (k, v)) =>
map + (k -> (v :: map(k)))
}
}
However, I would like to be able to use any Monoid instead of writing a method for List. For example, the values may be integers and I want to sum them instead of appending them in a list. Or they may be tuples (String, Int) where I want to accumulate the strings in a set but add the integers. How can I write such a method? Or is there something else I can use in scalaz to get this done?
Update: I wasn't as far away as I thought. I got a little bit closer, but I still don't know how to make it work if the values are tuples. Do I need to write yet another implicit conversion? I.e., one implicit conversion for each number of type parameters?
sealed trait SuperTraversable[T, U, F[_]]
extends scalaz.PimpedType[TraversableOnce[(T, F[U])]] {
def mergeKeys(implicit mon: Monoid[F[U]]): Map[T, F[U]] = {
value.foldLeft(Map[T, F[U]]().withDefaultValue(mon.zero)) {
case (map, (k, v)) =>
map + (k -> (map(k) |+| v))
}
}
}
implicit def superTraversable[T, U, F[_]](
as: TraversableOnce[(T, F[U])]
): SuperTraversable[T, U, F] =
new SuperTraversable[T, U, F] {
val value = as
}
First, while it's not relevant to your question, you are limiting your code's
generality by explicitly mentioning the type constructor F[_]. It works fine
without doing so:
sealed trait SuperTraversable[K, V]
extends scalaz.PimpedType[TraversableOnce[(K, V)]] {
def mergeKeys(implicit mon: Monoid[V]): Map[K, V] = {
value.foldLeft(Map[K, V]().withDefaultValue(mon.zero)) {
case (map, (k, v)) =>
map + (k -> (map(k) |+| v))
}
}
}
[...]
Now, for your actual question, there's no need to change mergeKeys to handle
funny kinds of combinations; just write a Monoid to handle whatever kind of
combining you want to do. Say you wanted to do your Strings+Ints example:
implicit def monoidStringInt = new Monoid[(String, Int)] {
val zero = ("", 0)
def append(a: (String, Int), b: => (String, Int)) = (a, b) match {
case ((a1, a2), (b1, b2)) => (a1 + b1, a2 + b2)
}
}
println {
List(
"a" -> ("Hello, ", 20),
"b" -> ("Goodbye, ", 30),
"a" -> ("World", 12)
).mergeKeys
}
gives
Map(a -> (Hello, World,32), b -> (Goodbye, ,30))