我想从项目列表中构建一个字典。
项目具有以下定义:
type Item =
| A of TotalPrice * Special
| B of TotalPrice * Special
| C of TotalPrice
| D of TotalPrice
我希望字典的键映射到案例ID:
| A
| B
| C
| D
然后我会将case id的值设为列表。
如何将案例ID与案例值分开?
示例:
let dictionary = items |> List.map (fun item -> item) // uh...
附录
module Checkout
(*Types*)
type UnitPrice = int
type Qty = int
type Special =
| ThreeForOneThirty
| TwoForFourtyFive
type TotalPrice = { UnitPrice:int ; Qty:int }
type Item =
| A of TotalPrice * Special
| B of TotalPrice * Special
| C of TotalPrice
| D of TotalPrice
(*Functions*)
let totalPrice (items:Item list) =
let dictionary = items |> List.map (fun item -> item) // uh...
0
(*Tests*)
open FsUnit
open NUnit.Framework
[<Test>]
let ``buying 2 A units, B unit, A unit = $160`` () =
// Setup
let items = [A ({UnitPrice=50; Qty=2} , ThreeForOneThirty)
B ({UnitPrice=30; Qty=1} , TwoForFourtyFive)
A ({UnitPrice=50; Qty=1} , ThreeForOneThirty)]
items |> totalPrice |> should equal 160
答案 0 :(得分:3)
您的数据针对您的用例进行了严格定义。如果您想自己引用各种项目,您需要自己定义它们:
type ItemKind = A | B | C | D
type Item = { Kind: ItemKind; Price: TotalPrice; Special: Special option }
然后您可以轻松地构建项目字典:
let dictionary = items |> List.map (fun i -> i.Kind, i) |> dict
虽然我必须注意到这样的字典可能不可能:如果items
列表包含多个相同类型的项目,其中一些不会包含在字典中,因为它不能包含多个相同的键。也许我不明白你追求的字典是什么。
答案 1 :(得分:1)
如果要使用A,B,C和D等键创建字典,则会失败,因为A和B是类型为TotalPrice * Special -> Item
的构造函数,而C和D是TotalPrice -> Item
类型的构造函数。字典必须对键的类型做出决定。
获取DU构造函数名称应该可以通过反射来实现,但对于你的情况是否真的有必要?
对于您的情况,也许不同的类型结构会更有效,即。 Fyodor Soikin提议。
答案 2 :(得分:0)
可能以下内容将澄清为何数据结构和代码不合适,因此也明确说明这主要与FP无关,如某些评论等所示。
我的猜测是这个问题与“如何分组”有关,而且实际上有一个groupBy函数!
(*Types*)
type UnitPrice = int
type Qty = int
type Special =
| ThreeForOneThirty
| TwoForFourtyFive
type TotalPrice = { UnitPrice:int ; Qty:int }
type Item =
| A of TotalPrice * Special
| B of TotalPrice * Special
| C of TotalPrice
| D of TotalPrice
let items = [A ({UnitPrice=50; Qty=2} , ThreeForOneThirty)
B ({UnitPrice=30; Qty=1} , TwoForFourtyFive)
A ({UnitPrice=50; Qty=1} , ThreeForOneThirty)]
let speciallyStupidTransformation =
function
| ThreeForOneThirty -> 34130
| TwoForFourtyFive -> 2445
let stupidTransformation =
function
| A (t,s) -> "A" + (s |> speciallyStupidTransformation |> string)
| B (t,s) -> "B" + (s |> speciallyStupidTransformation |> string)
| C (t) -> "C"
| D(t) -> "D"
let someGrouping = items |> List.groupBy(stupidTransformation)
val it : (string * Item list) list =
[("A34130",
[A ({UnitPrice = 50;
Qty = 2;},ThreeForOneThirty); A ({UnitPrice = 50;
Qty = 1;},ThreeForOneThirty)]);
("B2445", [B ({UnitPrice = 30;
Qty = 1;},TwoForFourtyFive)])]
是的,这还是一个坏主意。但它在某种程度上是独一无二的,可能会被进一步滥用以汇总一些金额或其他任何东西。
为此添加更多代码,如下所示:
let anotherStupidTransformation =
function
| A(t,_) -> (t.UnitPrice, t.Qty)
| B(t,_) -> (t.UnitPrice, t.Qty)
| C(t) -> (t.UnitPrice, t.Qty)
| D(t) -> (t.UnitPrice, t.Qty)
let x4y x y tp q =
if q%x = 0 then y*q/x else tp/q*(q%x)+(q-q%x)/x*y
let ``34130`` = x4y 3 130
let ``2445`` = x4y 2 45
let getRealStupidTotal =
function
| (s, (tp,q)) ->
(s|> List.ofSeq, (tp,q))
|> function
| (h::t, (tp,q)) ->
match t |> List.toArray |> System.String with
| "34130" -> ``34130`` tp q
| "2445" -> ``2445`` tp q
| _ -> tp
let totalPrice =
items
|> List.groupBy(stupidTransformation)
|> List.map(fun (i, l) -> i,
l
|> List.map(anotherStupidTransformation)
|> List.unzip
||> List.fold2(fun acc e1 e2 ->
((fst acc + e1) * e2, snd acc + e2) ) (0,0))
|> List.map(getRealStupidTotal)
|> List.sum
val totalPrice : int = 160
可能会也可能不会产生一些正确的测试用例。
对于上面的测试数据,据我所知,初始代码至少是可以的。总和确实是160 ......
我会在任何地方使用此代码吗?不。
可读吗?不。
可以修复吗?不是改变数据结构的方式,以避免几个愚蠢的转换......