listtupleselixir

How can i iterate a list in elixir to make another list?


I have a list of tuples in the following format

[{"string1",1},{"string2",2}...]

I'm gonna use that for another function but i realized i can't have that or rather i just think is too difficult to operate over that list in that format, so my solution was transforming that list into the following

["string1", "string2",...]

But i'm not sure how to do this as i'm still learning how Elixir works. My way of getting it was this:

      for x <- words do
        text ++ elem(words,0)
      end

"text" being an empty list and "words" being the list of tuples. But of course this doesn't work, not really sure why.


Solution

  • If you want to do it using for, you need to understand that for is not a construct to iterate over things, as in other programming languages. In Elixir, for is used for comprehensions, meaning that the result will be a data structure, created from an enumerable, like your list of tuples.

    You also need to understand that if you updated your code, text ++ elem(words, 0) wouldn't actually update text, and that ++ doesn't work the way you think it does either. ++ is useful for concatenating lists, or keyword lists, but not for adding single elements to a list. For that purpose you could do list ++ ["string"] or ["string"] ++ list which is faster; or even simpler: ["string" | list].

    And the reason it wouldn't update your list, is that in each "iteration" you would just be producing the concatenation, but you wouldn't actually be assigning that anywhere. And things in Elixir are not mutable, so ++ doesn't actually update something

    Now, in order to correctly create what you want using for, you could do it like this:

    iex> list = [{"string1", 1}, {"string2", 2}]
    [{"string1", 1}, {"string2", 2}]
    iex> for tup <- list, do: elem(tup, 0)
    ["string1", "string2"]
    iex> for {string, _} <- list, do: string
    ["string1", "string2"]
    

    Which basically means: using this list, create a new one, keeping only the first element of each tuple. Since a list is the default output of for. But you could also change the resulting data structure adding into: %{} or something else. Comprehensions are very powerful, you can get a better understanding of them here

    Another popular way to solve it would be using Enum.map, which maps a given enumerable to something else. Meaning that it transforms the elements of the enumerable, so you could do it like this as well:

    iex> list = [{"string1", 1}, {"string2", 2}]
    [{"string1", 1}, {"string2", 2}]
    iex> Enum.map(list, fn {string, _} -> string end)
    ["string1", "string2"]
    

    Here, the transformation would be made by the function which takes the tuple, matching the first element into something called string and "returns" just that for each element.

    Another simple way you could to it is using Enum.unzip/1 which takes a list of two-element tuples, like the ones you have, and produces a tuple with two elements. The first one being a list with the first element from each of your tuples, and the second one, a list with the second element from each of your tuples.

    So, you could do:

    iex> list = [{"string1",1},{"string2",2}]
    [{"string1", 1}, {"string2", 2}]
    iex> {strings, _} = Enum.unzip(list)
    {["string1", "string2"], [1, 2]}
    iex> strings
    ["string1", "string2"]
    

    This way, you would be left with a strings variable, containing the list you want. However, this only works with two element tuples, so if you have any tuple that has more than two, it wouldn't work. Besides, you wouldn't be using the second list, so the for comprehension here could suit you better.