聚合非规范化数据的惯用法

时间:2013-03-08 11:00:50

标签: f#

我有一组数组数组。作为一个例子

[[1,3],
 [4,3],
 [1,2],
 [7,2]]

我想将其转换为

[(3,[1,4])
 (2,[1,7])]

即:创建一个元组数组,其中第一个成员来自原始的索引1,而数组是基于索引1的原始分组的索引0的所有值。我可以强制解决这个问题,但是希望以更加FP的方式做到这一点

4 个答案:

答案 0 :(得分:5)

Seq.groupBy与一些地图结合使用可获得所需的结果

[[1;3];
 [4;3];
 [1;2];
 [7;2]] 
|> Seq.groupBy (fun (a::b) -> b) 
|> Seq.map (fun (a,b) -> a,b|> Seq.toList) 
|> Seq.map (fun (a,b) -> a,b|>List.map (fun (c::d) -> c ))

答案 1 :(得分:4)

F#是一种静态类型的函数式编程语言,因此您要做的第一件事就是将输入转换为有类型的表示形式,例如一对int的列表:

[ 1, 3
  4, 3
  1, 2
  7, 2 ]

然后你可以使用Seq.groupBy函数通过snd函数来管道它,以键入每对的第二个元素:

|> Seq.groupBy snd

这会为您提供[3, [1, 3; 4, 3]; ...]等,因此您希望使用fst函数在右侧绘制仅提取值(即剥离键):

|> Seq.map (fun (k, kvs) -> k, Seq.map fst kvs)

这会给出您想要的答案:[(3, [1; 4]); (2, [1; 7])]

答案 2 :(得分:2)

与@ John的答案类似,但假设内部集合是至少包含两个元素的数组:

[|[|1; 3|];
  [|4; 3|];
  [|1; 2|];
  [|7; 2|]|]
|> Seq.map (fun arr -> arr.[0], arr.[1])
|> Seq.groupBy snd
|> Seq.map (fun (k, v) -> k, Seq.map fst v)

// val it : seq<int * seq<int>> = seq [(3, seq [1; 4]); (2, seq [1; 7])]

答案 3 :(得分:1)

我的答案与上面的答案没有本质的不同,但它使用了一些组合逻辑,因此它看起来更像惯用语(对我来说)。此外,它还有一些有效性检查。

Apply2基本上是S combinator

let data = 
    [[1;3];
     [4;3];
     [1;2];
     [7;2]]

// Apply2 operator applies two functions to x
// and returns both results as a tuple
let (.&.) f g x = f x, g x

// A naive validator for sequences
let assert' predicate message xs =
    if not <| Seq.forall predicate xs then
        failwith message
    xs

let aggregate data =
    data
    // validate the input
    |> assert' (List.length >> (=) 2) "All elements must be of length of two"
    // essentially, convert a 2-element list to a tuple
    |> Seq.map (List.head .&. (List.tail >> List.head))
    // group over the second element of a tuple
    |> Seq.groupBy snd
    // we no longer need the key element in a tuple, so remove it
    |> Seq.map (fst .&. (snd >> Seq.map fst))

aggregate data |> printf "%A"