group :: Ord a => [(a, [b])] -> [(a, [b])]
我想查找具有相同fst的所有对,并将它们合并,通过将所有bs列表附加在一起,它们具有相同的a并丢弃unnessecary对等等...
我得到了:
group ((s, ls):(s', ls'):ps) =
if s == s'
then group ((s, ls++ls'):ps)
else (s, ls) : group ((s', ls'):ps)
group p = p
但显然这不会削减它,因为它不会对所有内容进行分组。
编辑: 示例
[("a", as),("c", cs), ("c", cs3), ("b", bs),("c", cs2), ("b", bs2)]
会输出
[("a", as),("c", cs++cs2++cs3),("b", bs++bs2)]
答案 0 :(得分:11)
barkmadley's answer的两种替代解决方案:
在注释中注释Tirpen,解决此问题的最佳方法取决于输入列表元组中不同第一个元素的数量 m 。对于 m 的小值,barkmadley使用Data.List.partition
是可行的方法。但是对于较大的值,算法的 O(n * m)的复杂度并不是那么好。在这种情况下, O(n log n)类型的输入可能会变得更快。因此,
import Data.List (groupBy, sortBy)
combine :: (Ord a) => [(a, [b])] -> [(a, [b])]
combine = map mergeGroup . myGroup . mySort
where
mySort = sortBy (\a b -> compare (fst a) (fst b))
myGroup = groupBy (\a b -> fst a == fst b)
mergeGroup ((a, b):xs) = (a, b ++ concatMap snd xs)
这会在barkmadley的输入上产生[("Dup",["2","3","1","5"]),("Non",["4"])]
。
或者,我们可以在Data.Map
:
import Data.Map (assocs, fromListWith)
combine :: (Ord a) => [(a, [b])] -> [(a, [b])]
combine = assocs . fromListWith (++)
这将产生[("Dup",["5","1","2","3"]),("Non",["4"])]
,这可能是也可能不是问题。如果是,那么又有两个解决方案:
首先使用Data.List.reverse
:
import Data.List (reverse)
import Data.Map (assocs, fromListWith)
combine :: (Ord a) => [(a, [b])] -> [(a, [b])]
combine = assocs . fromListWith (++) . reverse
Prepend(flip (++)
)而不是追加((++)
)(感谢barkmadley;我更喜欢这个解决方案):
import Data.Map (assocs, fromListWith)
combine :: (Ord a) => [(a, [b])] -> [(a, [b])]
combine = assocs . fromListWith (flip (++))
这两个定义都会导致combine
输出[("Dup",["2","3","1","5"]),("Non",["4"])]
。
作为最后一点,请注意combine
的所有这些定义都要求输入列表中元组的第一个元素是类Ord
的实例。 barkmadley的实现只要求这些元素是Eq
的实例。因此,存在可以由他的代码处理的输入,但不是由我的处理。
答案 1 :(得分:6)
import Data.List hiding (group)
group :: (Eq a) => [(a, [b])] -> [(a, [b])]
group ((s,l):rest) = (s, l ++ concatMap snd matches) : group nonmatches
where
(matches, nonmatches) = partition (\x-> fst x == s) rest
group x = x
此函数产生结果:
group [("Dup", ["2", "3"]), ("Dup", ["1"]), ("Non", ["4"]), ("Dup", ["5"])]
= [("Dup", ["2", "3", "1", "5"]), ("Non", ["4"])]
它的工作原理是将剩余的比特过滤成两个阵营,匹配的比特和不匹配的比特。然后它将那些匹配和递归的那些组合在一起。这实际上意味着输入列表中每个'key'的输出列表中将有一个元组。
答案 2 :(得分:0)
另一种解决方案,使用折叠来累积Map中的组。由于地图,这确实要求a
是Ord
的实例(BTW,您的原始定义要求a
是Eq
的实例,而barkmadley已将其合并到其中溶液)。
import qualified Data.Map as M
group :: Ord a => [(a, [b])] -> [(a, [b])]
group = M.toList . foldr insert M.empty
where
insert (s, l) m = M.insertWith (++) s l m
如果您是默默无闻的忠实粉丝,请将最后一行替换为:
insert = uncurry $ M.insertWith (++)
这省略了不必要的m
和uncurry
将(s, l)
对分成两个参数s
和l
。