Home > Back-end >  Append items to a list in a for loop using an immutable list?
Append items to a list in a for loop using an immutable list?

Time:01-05

val list = List()
for(i <- 1 to 10){
    list: i
}
println(list)

This ends up giving me an empty list although it should be filled with numbers from 1 to 10? I have a theory that it creates a new list each time due to the ":" operator but I am not entirely sure. I have solved the issue using a ListBuffer instead but I want to learn how to approach such a problem using immutable lists instead. Thank you.

CodePudding user response:

There is no single functional solution to this class of problem, but here are some options.

For the simple case in the question, you can do this

List.range(1,11) // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

If you want to calculate a different value for each element based on index, use tabulate:

List.tabulate(10)(x => x*3) // List(0, 3, 6, 9, 12, 15, 18, 21, 24, 27)

(You can pass a function if the logic is more complicated than this)

If you are building a list but are not sure whether you need every element, use Option and then flatten:

def genValue(i: Int): Option[Int] = ???

List.tabulate(10)(genValue).flatten

This will discard any values where genValue returns None and extract the Int where it returns Some(???).

If each operation may return a different number of elements, use List then flatten:

def genValue(i: Int): List[Int] = ???

List.tabulate(10)(genValue).flatten

This will take all the elements from all the List values returned by genValue and put them into a single List[Int].

If the length of the List is not known in advance then the best solution is likely to be a recursive function. While this may seem daunting to start with, it is worth learning how to use them as they are often the cleanest way of solving a problem.

CodePudding user response:

You cannot add an element (that is mutate the list) to an immutable list. You are right when you say:

I have a theory that it creates a new list each time

as a first step consider

      var list = List.empty[Int]
      
      for(i <- 1 to 10) {
        list = list :  i
      }

      println(list)

note that list is now a variable so that we can reassign value, but the list is still an immutable object. Infact for each iteratin we reassign to the variable list a new list with an element appended If you don't like the use of a variable you could use a fold operation, which is not much different from the for above, it still construct partial lists adding element one by one

      val result = (1 to 10).foldLeft(List.empty[Int]){ (partial_list, item) =>
        partial_list :  item
      }
      
      println(result)

CodePudding user response:

Here is the simplified signature of : :

def : (elem: B): List[B] 

It returns a new List with elem so it does not alter the current list.

To make this work switch to a ListBuffer i.e. something mutable:


import scala.collection.mutable.ListBuffer


val buffer = ListBuffer.empty[Int]

for(i <- 1 to 10) {
  buffer  = i
}

println(buffer)

If you want to keep the immutable List you could accumulate with fold:

val list = List.empty[Int]

(1 to 10)
  .foldLeft(list) { (acc, value) =>  acc :  value }
  •  Tags:  
  • Related