I have a Scala app with a list of items with checkboxes so the user select some, and click a button to shift them one position up (left). I decided to write a function to sh
I don't claim this stuff below to be efficient or readable. Unfortunately, all the good answers seem to be taken, so I'm going for originality. :-)
def shift[T](a: Seq[T], p: T => Boolean) = {
val (areP, notP) = a.zipWithIndex partition { case (t, index) => p(t) }
val shifted = areP map { case (t, index) => (t, index - 1) }
val others = notP map (shifted.foldLeft(_){
case ((t, indexNotP), (_, indexIsP)) =>
if (indexNotP == indexIsP) (t, indexNotP + 1) else (t, indexNotP)
})
(shifted ++ others).sortBy(_._2).map(_._1)
}
So, here's what's happening. First, I associate each character with its index (a.zipWithIndex
), and then separate then into areP
and notP
depending on whether the character satisfies p
or not.
So, at this point, I have two sequences, each composed of a character and its index in the original sequence.
Next, I simply shift the index of the elements in the first sequence, by subtracting 1, and compute shifted
.
Computing the new index of the unshifted elements is much harder. For each of those elements (notP map
), I'll do a foldLeft
. The accumulator of the fold left will be the element itself (always with its index). The sequence that is being folded is the sequence of shifted elements -- so one can see that for each unshifted element, I traverse the whole sequence of shifted elements (highly inefficient!).
So, we compare the index of the unshifted element to the index of each shifted element. If they are equal, increase the index of the unshifted element. Because the sequence of shifted elements is ordered (partition
doesn't change the order), we know that we'll test first for lower indices, and then for higher indices, guaranteeing that an element will have its index increased as much as necessary.
With that, we join the two sequences, order them by their new indices, and then map back to the element.
Here's a purely functional implementation
def shiftElements[A](l: List[A], pred: A => Boolean): List[A] = {
def aux(lx: List[A], accum: List[A]): List[A] = {
lx match {
case Nil => accum
case a::b::xs if pred(b) && !pred(a) => aux(a::xs, b::accum)
case x::xs => aux(xs, x::accum)
}
}
aux(l, Nil).reverse
}
And here's one that uses mutability on the inside to be faster
import scala.collection.mutable.ListBuffer
def shiftElements2[A](l: List[A], pred: A => Boolean): List[A] = {
val buf = new ListBuffer[A]
def aux(lx: List[A]) {
lx match {
case Nil => ()
case a::b::xs if pred(b) && !pred(a) => {
buf.append(b)
aux(a::xs)
}
case x::xs => {
buf.append(x)
aux(xs)
}
}
}
aux(l)
buf.toList
}
You could probably do this via a foldLeft
(also known as /:
):
(str(0).toString /: str.substring(1)) { (buf, ch) =>
if (ch.isUpper) buf.dropRight(1) + ch + buf.last else buf + ch
}
It needs work to handle the empty String but:
def foo(Str: String)(p: Char => Boolean) : String = (str(0).toString /: str.substring(1)) {
(buf, ch) => if (p(ch) ) buf.dropRight(1) + ch + buf.last else buf + ch
}
val pred = (ch: Char) => ch.isUpper
foo("abcDEfghI")(pred) //prints abDEcfgIh
I'll leave it as an exercise as to how to modify this into the array-based solution