如何优雅地合并两个具有指定键的元组seq?

时间:2019-01-16 10:39:23

标签: f#

我写了代码,但是看起来很丑。
图像有两个序列,如:

let x = [(1,"x");(2,"y")]
let y = [(1, "xx", "xxx");(2,"yy","yyy")]  

元素是元组,但形状不同。
结果必须为[(1, "x", "xx", "xxx");(2, "y", "yy", "yyy")]
元组中的第一个元素是这里的关键。

我编写的代码使用的地图如下:

let x = [(1,"x");(2,"y")]
let y = [(1, "xx", "xxx");(2,"yy","yyy")]  
let mapY = y |> Seq.map (fun (a,b,c) -> (a, (b, c))) |> Map.ofSeq

let r = [
    for (k,v) in x do
        let (v1,v2) = mapY |> Map.tryFind k |> Option.orElse (Some ("","")) |> Option.get
        yield (k, v, v1, v2)
]

printfn "%A" r

不太优雅。
因此,我想知道是否有更好的解决方案?(问题中的元组元素可以是seq类型,例如元组)。
关键问题是如何通过键优雅地合并两个seq。

2 个答案:

答案 0 :(得分:5)

我建议使用query计算表达式(有关更多信息和示例,请参见here)。在您的情况下,解决方案如下所示:

let r =
    query {
        for (k1, v) in x do
        join (k2, v1, v2) in y on (k1 = k2)
        select(k1, v, v1, v2) }
    |> Seq.toList

请注意,此处使用的join运算符实际上是内部联接。上面链接到的页面上列出了很多其他联接方法(以及其他运算符)。

答案 1 :(得分:2)

我同意@torbonde所示使用join可能是实现此目的的最佳方法。

如果您倾向于使用基本的List功能,那么我认为您所拥有的可能会尽可能的好。一个较小的调整是将orElse替换为defaultValue

let (v1,v2) = mapY |> Map.tryFind k |> Option.defaultValue ("","")

我要考虑的另一件事是,是否需要扁平元组-如果您对输入和输出端的元组嵌套感到满意,则可以执行以下操作(本质上仍然是您的原始代码,但是短一点):

let x = [(1,"x");(2,"y")]
let y = [(1, ("xx", "xxx"));(2,("yy","yyy"))]  

let mapY = Map.ofSeq y
let r = x |> List.map (fun (k, v) -> 
     k, v, defaultArg (mapY.TryFind k) ("",""))