Elm - 更新列表中的元素

Elm - update elements in a list

我刚开始使用 Elm 编程,但遇到了一些问题:

我想要一个可以更新列表中特定索引处元素字段的方法。

我的签名看起来像这样:

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

与:

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

在这种情况下,我想将给定索引处的任务的布尔值(焦点)设置为 true,并将列表中的所有其他任务设置为 false。

我已经在 Elm 中尝试使用数组,但后来我不得不使用 Maybe,我认为这不是一个好的解决方案。

我想我必须使用 'map' 来更改列表中的元素,但我不知道如何在特定索引处更改它。

谢谢!

因为你想更新列表中的所有元素(以确保所有元素要么为 False,而那些与 ID 匹配的元素为 True),你可以在列表上执行 List.map,同时提供一个函数其工作是检查索引并对元素执行更新。

这是一个示例,对您的示例代码进行了一些小改动:

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

我将您的签名更改为更通用。由于您没有提供 ID 是什么的指示,我假设元组中的第一个元素必须匹配第二个函数参数的类型。我还将 Task 替换为 MyTask 因为在 elm 中已经有一个常见的类型 Task.

我还要提到有一个 List.indexedMap 函数可以让您稍微简化函数声明。如果你在上面的例子中有一个元组输入和输出的唯一原因是你需要通过它的索引定位一个元素,那么使用 List.indexedMap 可能更容易。这是一个例子:

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

如您所见,它从函数中删除了一些元组样板文件,使其更加简洁。

现在您已经澄清了您的问题,真正的答案是乍得发布的两个更新的组合

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

如果您只想经常更改列表的第 n 个元素,List 将是错误的数据结构。 elm中的List实现为链表,在随机访问的性能方面表现不佳。

对于那种工作,您可能更应该使用榆树 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

关于该主题,this discussion on the elm bug tracker 可能会引起您的兴趣。