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)