【问题标题】:map multiple elements with same key in F#在 F# 中使用相同的键映射多个元素
【发布时间】:2021-12-18 15:09:11
【问题描述】:

假设两个相同长度的排序列表,键值列表let k = [1;1;2;2;3;3],一个数字列表let n = [1;2;3;4;5;6]。两个列表中的第 i 个位置相互映射,这意味着元素 n=1 的键值为 k=1n=2 的键值为 k=1... 以此类推。

如何在 F# 中创建一个映射多个具有相同键值的元素的列表?

所以输出格式为[[n1;n2;key];[n1;n2;key]...],示例输出为[[1;2;1];[3;4;2];[5;6;3]]

【问题讨论】:

    标签: arrays list algorithm arraylist f#


    【解决方案1】:

    你描述的是一个地图数据结构,所以我会使用一个,而不是用一堆列表函数自己实现它,你可以以这种方式转换你的两个列表

    let k = [1;1;2;2;3;3]
    let n = [1;2;3;4;5;6]
    
    let mapKeyToValues keys values =
        let folder m k v =
            m |> Map.change k (function
                | Some old -> Some (v :: old)
                | None     -> Some [v]
            )
        List.fold2 folder Map.empty keys values
    

    现在

    let r = mapKeyToValues k n
    

    你得到一个类似的数据结构

    Map [
        1, [2; 1] 
        2, [4; 3]
        3, [6; 5]
    ]
    

    这相当于 JSON 对象

    {
        "1": [2,1],
        "2": [4,3],
        "3": [6,5]
    }
    

    通常你可以写

    let r = Map (List.zip k n)
    

    这样,您可以从带有(k,v) 的元组中创建这样一个映射数据结构。但是一个新的密钥,只是覆盖了一个旧的,所以你得到

    Map [
        (1, 2)
        (2, 4)
        (3, 6)
    ]
    

    这就是您需要Map.change 的原因。您使用List.fold2 遍历列表中的每个(key,value),然后将其添加到地图数据结构中。如果键不存在(None 情况),则创建一个列表作为具有唯一值的值。在Some 的情况下,已经添加了一个键,您将值添加到已经存在的列表中。

    在此示例中,我认为值的顺序无关紧要。如果您希望它们出现在值列表中的顺序相同,则必须使用List.foldBack2 而不是List.fold2。但是您必须更改某些参数的顺序。这对你来说应该是一个练习,以便更好地理解它。

    有一个Map 模块具有不同的功能来处理这样的数据结构,它还提供了Map.mapMap.filterMap.foldMap.find、...等.所以改用这个。如果确实需要,您也可以使用Map.toList 再次将其转换回列表,或使用Map.fold


    无论如何,作为提醒,有些人使用(List.zip 然后List.map)或(List.zip 然后List.fold)。有一个 List.map2List.fold2 在一次操作中完成此操作,而不是两次。

    【讨论】:

      【解决方案2】:

      认为您正在寻找的是Seq.zip,它接受两个序列(列表是序列)并返回一个对序列。

      另请注意,您使用逗号指定列表,但 F# 使用分号分隔列表元素。

      let keys = [ 1; 1; 2; 2; 3; 3 ]
      let values = [ 1; 2; 3; 4; 5; 6 ]
      
      let map =
        values
        |> Seq.zip keys
        |> Seq.groupBy (fun (k, v) -> k)
        |> Seq.map (fun (k, vs) -> k, vs |> Seq.map snd |> Seq.toList)
        |> Map.ofSeq
      
      printfn "%A" map
      
      // map [(1, [1; 2]); (2, [3; 4]); (3, [5; 6])]
      

      要获得您正在寻找的格式(尽管我认为您应该考虑使用Map),您可以像这样解压缩它:

      let mapList =
        map
        |> Map.toSeq
        |> Seq.map (fun (k, vs) -> vs @ [ k ])
        |> Seq.toList
      
      printfn "%A" mapList
      
      // [[1; 2; 1]; [3; 4; 2]; [5; 6; 3]]
      

      【讨论】:

        【解决方案3】:

        这可以通过将两个列表“压缩”在一起然后按键分组来实现:

        let groupByKey keys numbers =
            List.zip keys numbers
            |> List.groupBy fst
            |> List.map (fun (key, values) -> key, (values |> List.map snd))
        
        groupByKey k n // [(1, [1; 2]); (2, [3; 4]); (3, [5; 6])]
        

        上述版本的函数没有您指定的确切输出格式,但它是一个更加结构化的输出,其中列表中的每个项目是键的元组和该键的值列表。对于更简洁的代码,这可能是首选,具体取决于您要对结果执行的操作。

        为了得到你指定的格式,可以稍微改变一下:

        let groupByKey keys numbers =
            List.zip keys numbers
            |> List.groupBy fst
            |> List.map (fun (key, values) -> [
                yield! values |> List.map snd
                yield key 
            ])
        
        groupByKey k n // [[1; 2; 1]; [3; 4; 2]; [5; 6; 3]]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-03-12
          • 2012-09-17
          • 2020-12-14
          • 1970-01-01
          • 2013-11-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多