假设我有一个像这样的值列表:
["abc","abd","aab","123"]
我希望通过使用将任何元素映射到键的函数将这些值分组到Haskell中的MultiMap(概念上,不限于特定的数据结构)。
对于此示例,我们将使用take 2
作为映射器。
我打算得到的结果(概念上,作为JSON):
{"ab":["abc","abd"], "aa":["aab"], "12":["123"]}
在此示例中,我将使用[(String, [String])]
作为Multimap数据结构。
我的基本想法(概念上):
let datalist = ["abc","abd","aab","123"]
let mapfn = take 2
let keys = nub $ map mapfn datalist
let valuesForKey key = filter ((==key).mapfn) datalist
let resultMultimap = zip keys $ map valuesForKey keys
我的问题:
filter
)?从概念上讲,这个问题类似于SQL GROUP BY
语句。
答案 0 :(得分:8)
使用fromListWith
中的Data.Map
:
> let xs = ["abc","abd","aab","123"]
> let f = take 2
> Data.Map.fromListWith (++) [(f x, [x]) | x <- xs]
fromList [("12",["123"]),("aa",["aab"]),("ab",["abd","abc"])]
答案 1 :(得分:2)
编辑2014-03-28:我的功能现已发布在Hackage上,请参阅group-with
欢迎基于hammar's excellent answer我将两个可重用的函数放在一起来解决这个问题。
groupWith
完全解决了我的要求。 groupWithMulti
通过允许标识符生成函数(例如我的示例中的take 2
)为单个值返回多个标识符来概括该概念(在我的示例中,值为{{1}之一) }),或者根本没有。
该值将添加到["abc","abd","aab","123"]
生成的任何标识符的Map
值。
f
只需使用import Data.Map (Map)
import qualified Data.Map as Map
-- | Group values in a list by their identifier, being returned
-- by a given function. The resulting map contains,
-- for each generated identifier the values (from the original list)
-- that yielded said identifier by using the function
groupWith :: (Ord b) => (a -> b) -> [a] -> (Map b [a])
groupWith f xs = Map.fromListWith (++) [(f x, [x]) | x <- xs]
-- | Like groupWith, but the identifier-generating function
-- may generate multiple outputs (or even none).
-- The corresponding value from the original list will be placed
-- in the identifier-corresponding map entry for each generated
-- identifier
groupWithMulti :: (Ord b) => (a -> [b]) -> [a] -> (Map b [a])
groupWithMulti f xs =
let identifiers x = [(val, [x]) | val <- f x]
in Map.fromListWith (++) $ concat [identifiers x | x <- xs]
将这些函数的结果转换回元组列表。
当我有空闲时间时,我将尝试在内存数据分组中使用此方法在Hackage上创建一个通用库。