I am not able to add another element to a list

Hello, I am trying to add another element to a list and for some reason it is giving me errors. I am new to elixir and I have just started, please help me figure this out.
Code:

def forloop(list, y) do
    if y > 0 do
      list = Enum.map list, fn(x) ->
        x - 1
        if (x == 0) do
          x + 6
          list = list ++ [9]
        end
      end

      forloop(list, y - 1)
    end
  end

The error is saying variable list is not used.

Inside Enum.map you cannot change the full list (list = list ++ [9]) - you can only change the current item (x).

So what is a better way of adding an element if x is equal to 0?

If you need to change the full list for each item, then you must look into an Enum.reduce based solution.

How will that help? After 1 loops if the starting list was [1, 2, 4], I want the new value to be [6, 1, 3, 8] and after another loop it would be [5, 6, 2, 7, 8].

Enum.reduce takes a function like Enum.map but the function has two arguments, the current item, and the accumulator. So you could use the list as the accumulator.

But that wouldn’t map it unto list like map would.

Plot twist: Enum.map is syntactic sugar for Enum.reduce

Well when I try to do:

list = Enum.reduce list, list, fn(x, acc) ->
        x - 1
        if (x == 0) do
          x + 6
          acc ++ [9]
        end
      end

it gives me an error

Values in elixir are not mutable. You’re doing a lot of things and then just throwing the answer away.

list = Enum.reduce list, list, fn(x, acc) ->
        x - 1   # result gets thrown away
        if (x == 0) do
          x + 6  # result gets thrown away
          acc ++ [9]
        end  # result is nil if (x != 0)
      end
1 Like

Ok, so from what I understood I made the code this:

list = Enum.map list, fn(x) ->
        x = x - 1
        if (x == 0) do
          x + 6
        else
          x
        end
      end

This doesn’t help me with adding a new element to the list but it works for x. Can you help me make it add a new element? I tried this and it didn’t work:

list = Enum.reduce list, list, fn(x, acc) ->
        x = x - 1
        if (x == 0) do
          x + 6
          acc ++ [9]
        else
          x
        end
      end

When you reduce, you are building the list back up from scratch. This allows you to both update the existing items, and add new items. The result of your anonymous function should always be the new, updated acc. Try:

list =
  Enum.reduce(list, [], fn item, acc ->
    case item - 1 do
      0 ->
        [6, 9 | acc]
      x ->
        [x | acc]
    end
  end)
2 Likes

This is your code “translated”

defmodule IAmCool do
  def forloop(list0, y) do
    if y > 0 do
      list1 =
        Enum.map(list0, fn x0 ->
          x0 - 1

          if x0 == 0 do
            x0 + 6
            list2 = list0 ++ [9]
          end
        end)

      forloop(list1, y - 1)
    end
  end
end

warning: variable “list2” is unused (if the variable is not meant to be used, prefix it with an underscore)
#cell:10: IAmCool.forloop/2

Also x + 6 does does nothing if you don’t assign it to a variable. also due to lexical scope the variables assignment do not escape the if block.

1 Like

maybe more descriptive variable names will help

starting_list = []
new_list =
  Enum.reduce(source_list, starting_list, fn source_item, list_so_far ->
    case source_item - 1 do
      0 ->
        [6, 9 | list_so_far]
      other_number ->
        [other_number | list_so_far]
    end
  end)
2 Likes

Please if you don’t mind, can you translate it to an if statement instead of case statement. It works fine but I want to better understand this solution.

case source_item - 1 do
  0 ->
    [6, 9 | list_so_far]
  other_number ->
    [other_number | list_so_far]
end

is equivalent to

if source_item - 1 == 0 do
  [6, 9 | list_so_far]
else
  [source_item - 1 | list_so_far]
end

As you can see, it removes the need to do source_item - 1 twice, because it binds the result to other_number (in those cases where it is not 0)

2 Likes
defmodule IAmCool do
  # First thing, SnakeCase is used for Modules, undercore is use for function names and variables.
  # Secondly, use pattern matching to avoid ives (if)
  def for_loop(list0, y) when y > 0 do
    list1 =
      Enum.map(list0, fn x0 ->
        # Assign the result of x+1 to a variable
        x1 = x0 - 1

        # read inside the if block to know why I do this.
        {list3, x3} =
          if x1 == 0 do
            # Assign the result of x+6 to a variable
            x2 = x1 + 6

            # here you want to modify the value of an outter variable (list0) so
            # doing: list0 = list0 ++ [9]
            # will not work as variables do not leak out if blocks in Elixir.
            # so we just return and assign the result to variables via pattern matching
            list2 = list0 ++ [9]

            {list2, x2}
          end

        # alight, now we return list0
        # and the question is. What do you need x3 for?
        list3
      end)

    for_loop(list1, y - 1)
  end

  def for_loop(_list0, _y),
    do: nil
end

Pattern matching can be used to avoid the case/if/etc conditional.

starting_list = []
new_list =
  Enum.reduce(source_list, starting_list, fn
    source_item, list_so_far when source_item - 1 == 0  ->
        [6, 9 | list_so_far]

     source_item, list_so_far ->
        [source_item - 1 | list_so_far]
    end
  end)

and even more simplified

starting_list = []
new_list =
  Enum.reduce(source_list, starting_list, fn
    1, list_so_far  ->
        [6, 9 | list_so_far]

     source_item, list_so_far ->
        [source_item - 1 | list_so_far]
    end
  end)

NOTE: remember to Enum.reverse() your list after Enum.reduce()

1 Like