有人可以告诉我如何更新嵌套记录中的子项目吗?
我想将值=“ B”的项目的isSelected
设置为true
type MyItem = {isSelected:bool; value:string}
type MyModel = {list:MyItem list}
let a = {isSelected = false; value = "A"}
let b = {isSelected = false; value = "B"}
let c = {isSelected = false; value = "C"}
let m = {list = [a;b;c]}
let m2 = { m with list = { m.list with ??? = { ??? }}}
我不会使用可变数据结构。
答案 0 :(得分:1)
使用List.map
:
let m2 =
{ m with list =
List.map (fun item ->
if item.value = "B" then
{ item with isSelected = true }
else
item)
m.list
}
这将创建一个新列表,其中每个项目均与以前相同,但我们要“更新”的项目除外,因为我们将其替换为isSelected
为true
的新项目。
答案 1 :(得分:1)
不可移植性很好,但是当处理嵌套的不可更改结构时,它可能会变得有些毛茸茸。尤其是如果它嵌套得很深。
解决这个问题的一种方法就是所谓的镜头。
因此,我稍微提高了示例的嵌套级别,以使镜头的值更清晰可见。
module Lenses =
// This lens is a pair of function, a getter that get's inner value of an object
// and a setter that sets the inner value of an object
// The cool thing is that a lens is composable meaning we can create a lens
// that allows us to get and set a deeply nested property succinctly
type Lens<'O, 'I> = L of ('O -> 'I)*('I -> 'O -> 'O)
let lens (g : 'O -> 'I) (s : 'I -> 'O -> 'O) = L (g, s)
// Gets an inner value
let get (L (g, _)) o = g o
// Sets an inner value
let set (L (_, s)) i o = s i o
// Updates an inner value given an updater function that sees the
// inner value and returns a new value
let over (L (g, s)) u o = s (u (g o)) o
// Compose two lenses into one, allows for navigation into deeply nested structures
let compose (L (og, os)) (L (ig, is)) =
let g o = ig (og o)
let s i o = os (is i (og o)) o
L (g, s)
type Lens<'O, 'I> with
static member (-->) (o, i) = compose o i
open Lenses
// I made the model a bit more complex to show benefit of lenses
type MySelection =
{
isSelected: bool
}
// Define a lens that updates the property, this code can potentially be generated
// Scala does this with macros, in F# there are other possibilities
static member isSelectedL : Lens<MySelection, bool> = lens (fun o -> o.isSelected) (fun i o -> { o with isSelected = i })
type MyValue =
{
value: string
}
static member valueL : Lens<MyValue, string> = lens (fun o -> o.value) (fun i o -> { o with value = i })
type MyItem =
{
selection : MySelection
value : MyValue
}
static member selectionL : Lens<MyItem, MySelection> = lens (fun o -> o.selection) (fun i o -> { o with selection = i })
static member valueL : Lens<MyItem, MyValue> = lens (fun o -> o.value ) (fun i o -> { o with value = i })
type MyModel =
{
list: MyItem list
}
static member listL : Lens<MyModel, MyItem list> = lens (fun o -> o.list) (fun i o -> { o with list = i })
[<EntryPoint>]
let main argv =
// Define example model
let a = {selection = {isSelected = false}; value = {value = "A"}}
let b = {selection = {isSelected = false}; value = {value = "B"}}
let c = {selection = {isSelected = false}; value = {value = "C"}}
let m = {list = [a;b;c]}
// Print it
printfn "%A" m
// Update the model
let m2 =
let mapper (v : MyItem) =
// Grabs the nest value using lens composition
let nestedValue = v |> get (MyItem.valueL --> MyValue.valueL)
let isSelected = nestedValue = "B"
// Set the nested isSelected using lens composition
v |> set (MyItem.selectionL --> MySelection.isSelectedL) isSelected
// Maps nested list property
m |> over MyModel.listL (List.map mapper)
printfn "%A" m2
0