Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala insert into list at specific locations

This is the problem that I did solve, however being a total imperative Scala noob, I feel I found something totally not elegant. Any ideas of improvement appreciated.

val l1 = 4 :: 1 :: 2 :: 3 :: 4 :: Nil // original list
val insert = List(88,99) // list I want to insert on certain places

// method that finds all indexes of a particular element in a particular list
def indexesOf(element:Any, inList:List[Any]) = {
        var indexes = List[Int]()
        for(i <- 0 until inList.length) {
                if(inList(i) == element) indexes = indexes :+ i
        }
        indexes
}


var indexes = indexesOf(4, l1) // get indexes where 4 appears in the original list

println(indexes)

var result = List[Any]()

// iterate through indexes and insert in front
for(i <- 0 until indexes.length) {
        var prev = if(i == 0) 0 else indexes(i-1)
        result = result ::: l1.slice(prev, indexes(i)) ::: insert
}
result = result ::: l1.drop(indexes.last) // append the last bit from original list

println(result)

I was thinking more elegant solution would be achievable with something like this, but that's just pure speculation.

var final:List[Any] = (0 /: indexes) {(final, i) => final ::: ins ::: l1.slice(i, indexes(i))
like image 435
Murgh Avatar asked Jan 12 '11 23:01

Murgh


1 Answers

def insert[A](xs: List[A], extra: List[A])(p: A => Boolean) = {
  xs.map(x => if (p(x)) extra ::: List(x) else List(x)).flatten
}

scala> insert(List(4,1,2,3,4),List(88,99)){_ == 4}
res3: List[Int] = List(88, 99, 4, 1, 2, 3, 88, 99, 4)

Edit: explanation added.

Our goal here is to insert a list (called extra) in front of selected elements in another list (here called xs--commonly used for lists, as if one thing is x then lots of them must be the plural xs). We want this to work on any type of list we might have, so we annotate it with the generic type [A].

Which elements are candidates for insertion? When writing the function, we don't know, so we provide a function that says true or false for each element (p: A => Boolean).

Now, for each element in the list x, we check--should we make the insertion (i.e. is p(x) true)? If yes, we just build it: extra ::: List(x) is just the elements of extra followed by the single item x. (It might be better to write this as extra :+ x--add the single item at the end.) If no, we have only the single item, but we make it List(x) instead of just x because we want everything to have the same type. So now, if we have something like

4 1 2 3 4

and our condition is that we insert 5 6 before 4, we generate

List(5 6 4) List(1) List(2) List(3) List(5 6 4)

This is exactly what we want, except we have a list of lists. To get rid of the inner lists and flatten everything into a single list, we just call flatten.

like image 125
Rex Kerr Avatar answered Oct 01 '22 12:10

Rex Kerr