F# 确定共享键的所有(键*值)列表条目是否具有相同的奇异值的惯用方法
F# idiomatic way of determining if all (key*value) list entries sharing key have same singular distinct value
给定一组 int*string
的词典,其中第一个是“主要”,我想回答这个问题:
For all additional dictionaries, do they all have the same values for the same keys as the primary?
我目前通过执行以下操作实现了这一目标:
let allSame = primary @ remaining
|> Seq.groupBy (fun (pos, _) -> pos)
|> Seq.map (fun (pos, items) -> (pos, items |> Seq.map (fun (_, name) -> name) |> Seq.distinct |> List.ofSeq))
|> Seq.exists (fun (_, names) -> names.Length > 1))
我想知道是否有更惯用的方法来实现这一目标?
在分组 int * (int * string) list
中重复 pos
然后不得不减少到 int * string list
似乎有点多余,但不幸的是 groupBy
没有提供价值预测超载。
编辑
给定一堆具有简化结构的项目(字段){SortOrder:int;Name:string;...}
我要去:Field list -> (int * string) list
“主要”只是列表的头部,我选择哪个作为“主要”并不重要,因为我只对具有相同位置的所有字段是否也具有相同的名称感兴趣。
这就是为什么我按位置分组,然后减少到一个不同的名称列表,并且只计算条目数(>1 显然意味着一些分歧)。
最终解决方案
这是我最终得到的结果:
let primary = getFields <| fst x
let allSame = (primary) @ ((tail |> List.map (fun (m,_) -> getFields m)) |> List.collect (fun e -> e))
|> Seq.sortBy (fun (pos, _) -> pos)
|> Seq.pairwise
|> Seq.forall (fun ((_,namex),(_,namey)) -> Seq.forall2 (=) namex namey)
if allSame then
Some (fst x)
else
failwith "Some error message here"
就像@Carsten 在他的评论中所说的那样,按键排序然后比较每个KeyValuePair。作为一个额外的好处,Seq.forall
是惰性的并在第一次不匹配时停止评估。
[primary; remaining1; remaining2]
|> Seq.map (Seq.sortBy (fun (KeyValue(k,_)) -> k))
|> Seq.pairwise
|> Seq.forall (fun (x, y) -> Seq.forall2 (=) x y)
为了纯粹的可读性,我更愿意定义一个辅助函数来测试每个 key/value 对。
你的问题没有说明不匹配的密钥是否应该可以,所以请选择合适的:
(密钥不匹配是可以的)
let looselyAllSame (primary :: remaining) =
let hasDifferentName key value =
primary |> Map.tryFind key |> Option.exists ((<>) value)
not (remaining |> List.exists (Map.exists hasDifferentName))
(密钥不匹配是不行的)
let strictlyAllSame (primary :: remaining)=
let hasSameName key value =
primary |> Map.tryFind key |> Option.exists ((=) value)
remaining |> List.forall (Map.forall hasSameName)
给定一组 int*string
的词典,其中第一个是“主要”,我想回答这个问题:
For all additional dictionaries, do they all have the same values for the same keys as the primary?
我目前通过执行以下操作实现了这一目标:
let allSame = primary @ remaining
|> Seq.groupBy (fun (pos, _) -> pos)
|> Seq.map (fun (pos, items) -> (pos, items |> Seq.map (fun (_, name) -> name) |> Seq.distinct |> List.ofSeq))
|> Seq.exists (fun (_, names) -> names.Length > 1))
我想知道是否有更惯用的方法来实现这一目标?
在分组 int * (int * string) list
中重复 pos
然后不得不减少到 int * string list
似乎有点多余,但不幸的是 groupBy
没有提供价值预测超载。
编辑
给定一堆具有简化结构的项目(字段){SortOrder:int;Name:string;...}
我要去:Field list -> (int * string) list
“主要”只是列表的头部,我选择哪个作为“主要”并不重要,因为我只对具有相同位置的所有字段是否也具有相同的名称感兴趣。
这就是为什么我按位置分组,然后减少到一个不同的名称列表,并且只计算条目数(>1 显然意味着一些分歧)。
最终解决方案
这是我最终得到的结果:
let primary = getFields <| fst x
let allSame = (primary) @ ((tail |> List.map (fun (m,_) -> getFields m)) |> List.collect (fun e -> e))
|> Seq.sortBy (fun (pos, _) -> pos)
|> Seq.pairwise
|> Seq.forall (fun ((_,namex),(_,namey)) -> Seq.forall2 (=) namex namey)
if allSame then
Some (fst x)
else
failwith "Some error message here"
就像@Carsten 在他的评论中所说的那样,按键排序然后比较每个KeyValuePair。作为一个额外的好处,Seq.forall
是惰性的并在第一次不匹配时停止评估。
[primary; remaining1; remaining2]
|> Seq.map (Seq.sortBy (fun (KeyValue(k,_)) -> k))
|> Seq.pairwise
|> Seq.forall (fun (x, y) -> Seq.forall2 (=) x y)
为了纯粹的可读性,我更愿意定义一个辅助函数来测试每个 key/value 对。
你的问题没有说明不匹配的密钥是否应该可以,所以请选择合适的:
(密钥不匹配是可以的)
let looselyAllSame (primary :: remaining) =
let hasDifferentName key value =
primary |> Map.tryFind key |> Option.exists ((<>) value)
not (remaining |> List.exists (Map.exists hasDifferentName))
(密钥不匹配是不行的)
let strictlyAllSame (primary :: remaining)=
let hasSameName key value =
primary |> Map.tryFind key |> Option.exists ((=) value)
remaining |> List.forall (Map.forall hasSameName)