我有一个map reduce代码,我通过某个键将每个线程分组,然后在reduce部分合并结果。我当前的方法是在累加器中搜索特定的键索引,然后mapi只检索此键的组合结果,其余部分保持不变:
let rec groupFolder sequence acc =
match sequence with
| (by:string, what) :: rest ->
let index = acc |> Seq.tryFindIndex( fun (byInAcc, _) -> byInAcc.Equals(by) )
match index with
| Some (idx) ->
acc |> Seq.mapi( fun i (byInAcc, whatInAcc) -> if i = idx then (by, (what |> Array.append whatInAcc) ) else byInAcc, whatInAcc )
|> groupFolder rest
| None -> acc |> Seq.append( seq{ yield (by, what) } )
|> groupFolder rest
我的问题是,这是一种更实用的方法吗?
作为此减速器的示例输入
let GroupsCommingFromMap = [| seq { yield! [|("key1", [|1;2;3|] ); ("key2", [|1;2;3|] ); ("key3", [|1;2;3|]) |] }, seq { yield! [|("key1", [|4;5;6|] ); ("key2", [|4;5;6|] ); ("key3", [|4;5;6|]) |] } |];;
GroupsCommingFromMap |> Seq.reduce( fun acc i ->
acc |> groupFolder (i |> Seq.toList))
预期结果应包含所有key1..key3,每个都包含数组1..6
答案 0 :(得分:2)
根据您发布的代码,您不太清楚自己要做什么。你能包含一些样本输入(以及你想要的输出)吗?你的代码实际上是否适用于任何输入(它具有不完整的模式匹配,所以我怀疑......)
无论如何,您可以使用Seq.groupBy
实现基于密钥的地图缩减。例如:
let mapReduce mapper reducer input =
input
|> Seq.map mapper
|> Seq.groupBy fst
|> Seq.map (fun (k, vs) ->
k, vs |> Seq.map snd |> Seq.reduce reducer)
下面:
mapper
从输入序列中获取一个值并将其转换为键值对。然后mapReduce
函数使用键reducer
用于减少与每个键相关联的所有值这可以让你创建一个像这样的字数统计函数(使用简单的映射器,将单词作为键返回,1为值,reducer只添加所有数字):
"hello world hello people hello world".Split(' ')
|> mapReduce (fun w -> w, 1) (+)
编辑:您提到的示例并没有" mapper"部分,但它有数组数组作为输入 - 所以也许更容易直接使用Seq.groupBy
这样写:
let GroupsCommingFromMap =
[| [|("key1", [|1;2;3|] ); ("key2", [|1;2;3|] ); ("key3", [|1;2;3|]) |]
[|("key1", [|4;5;6|] ); ("key2", [|4;5;6|] ); ("key3", [|4;5;6|]) |] |]
GroupsCommingFromMap
|> Seq.concat
|> Seq.groupBy fst
|> Seq.map (fun (k, vs) -> k, vs |> Seq.map snd |> Array.concat)