Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elm - update elements in a list

I just started programming in Elm and am stuck at something:

I would like to have a method that can update fields of elements in a list at a certain index.

My signature would look like this:

updateElement : List (ID, Task) -> Int -> List (ID, Task)

with:

type alias Task =
  { description : String, focus : Bool}

In this case I would like to set the boolean (focus) of the task at the index given to true and all the others tasks in the list to false.

I already tried with arrays in Elm but then I have to work with Maybe and don't think that is a good solution.

I suppose I will have to work with 'map' to change elements in my list but I don't have any clue how I could change it at a particular index.

Thanks!

like image 759
Thibault Avatar asked Dec 29 '15 00:12

Thibault


People also ask

How to use list in Elm?

The List, Tuples and Record data structures can be used to store a collection of values. This chapter discusses how to use List in Elm. A List is a collection of homogeneous values. The values in a list must all be of the same data type. Consider the following limitations while using variables to store values − Variables are scalar in nature.

How to update all elements in a list of elements?

Since you want to update all elements in the list (to make sure all elements are either False while those matching the ID are True), you can perform a List.map over the list, while supplying a function whose job is to check the index and perform the update on the element. Here's an example with a few minor changes to your example code:

What are the different types of data structures in Elm?

In Elm there are different kinds of data structures that can contain elements. This article spotlights the iterable structures lists, arrays, sets and dictionaries, which support the basic operations of functional programming like map, filter and folding/reducing.

How to check the signature of function in Elm REPL?

This function returns the first element from input list. To check the signature of function, type the following in elm REPL − This function returns all elements after first in the list. The cons operator ( :: ) adds an element to the front of a list. The new element to be added and the data-type of the values in the list must match.


Video Answer


3 Answers

Now that you've clarified your question, the real answer is a combination of the two updates Chad posted

updateElement : List (ID, Task) -> Int -> List (ID, Task)
updateElement list indexToFocusOn =
  let
    toggle index (id, task) =
      if index == indexToFocusOn then
        (id, { task | focus = true })
      else
        (id, { task | focus = false })
  in
    List.indexedMap toggle list
like image 130
robertjlooby Avatar answered Sep 19 '22 09:09

robertjlooby


If you want often to change only the nth element of a list, a List would be the wrong data structure. A List in elm is implemented as a linked list, which will not fare well in terms of performance with random access.

For that kind of work, you probably should rather use an elm Array, and indeed the Array does have a simple function to set the nth element, leaving all the others untouched: Array.set :: Int -> a -> Array a -> Array a.

On that topic, this discussion on the elm bug tracker could be of interest.

like image 33
Emmanuel Touzery Avatar answered Sep 20 '22 09:09

Emmanuel Touzery


Since you want to update all elements in the list (to make sure all elements are either False while those matching the ID are True), you can perform a List.map over the list, while supplying a function whose job is to check the index and perform the update on the element.

Here's an example with a few minor changes to your example code:

type alias MyTask =
  { description : String
  , focus : Bool
  }

updateElement : List (a, MyTask) -> a -> List (a, MyTask)
updateElement list id =
  let
    toggle (idx, task) =
      if id == idx then
        (idx, { task | focus = True })
      else
        (idx, { task | focus = False })
  in
    List.map toggle list

I changed your signatures to be more generic. Since you provided no indication of what ID was, I assumed that the first element in the tuple had to match the type of whatever the second function parameter was. I also replaced Task with MyTask since there's already a common type in elm called Task.

I'll also mention that there is a List.indexedMap function which could let you simplify your function declaration a little bit. If the only reason you have a tuple input and output in your example above is because you need to locate an element by its index, it's probably easier to use List.indexedMap. Here's an example:

updateElement2 : List MyTask -> Int -> List MyTask
updateElement2 list id =
  let
    toggle idx task =
      if id == idx then
        { task | focus = True }
      else
        { task | focus = False }
  in
    List.indexedMap toggle list

As you can see, it cuts some of that tuple boilerplate out of the function, making it a bit cleaner.

like image 34
Chad Gilbert Avatar answered Sep 19 '22 09:09

Chad Gilbert