我有一个这样的清单:
[(1,[2]), (1,[3]), (1,[4]), (2,[1]), (2,[2]), (3,[1])]
我想要的是一个以列表中的第一个元素开头的函数,如果它在列表中找到了与元组类似(fst)的任何其他元素,那么它将找到的元素的(snd)连接到第一个元素的(snd)。在上面显示的示例中,最终结果将是以下列表:
[(1,[2,3,4]), (2,[1,2]), (3,[1])]
我尝试了很多选项却没有成功。有什么建议?
由于
答案 0 :(得分:2)
import Data.List (groupBy)
import Data.Function (on)
import Control.Arrow ((***))
f = map ((head *** concat) . unzip) . groupBy ((==) `on` fst)
> let l = [(1,[2]), (1,[3]), (1,[4]), (2,[1]), (2,[2]), (3,[1])]
> groupBy ((==) `on` fst) l
[[(1,[2]),(1,[3]),(1,[4])],[(2,[1]),(2,[2])],[(3,[1])]]
> map unzip $ groupBy ((==) `on` fst) l
[([1,1,1],[[2],[3],[4]]),([2,2],[[1],[2]]),([3],[[1]])]
您可以使用Control.Arrow中的(***)来操作元组
> (pred *** succ) (4,5)
(3,6)
> let f = map ((head *** concat) . unzip) . groupBy ((==) `on` fst)
> f l
[(1,[2,3,4]),(2,[1,2]),(3,[1])]
答案 1 :(得分:1)
Prelude> :m + Data.Function Data.List
Prelude> let xs = [(1,[2]), (1,[3]), (1,[4]), (2,[1]), (2,[2]), (3,[1])]
Prelude> map (\lst -> (fst $ head lst, concat $ map snd lst)) (groupBy ((==) `on` fst) xs)
[(1,[2,3,4]),(2,[1,2]),(3,[1])]
答案 2 :(得分:0)
请注意,如果相同值的键不连续,groupBy
将不会分组。例如:
\> groupBy (==) [1, 2, 1]
[[1],[2],[1]]
因此,您需要确保传入已排序的列表。
另一种方法是使用Data.Map
和右折:
import Data.Map (empty, alter, toList)
fun :: (Foldable t, Ord k) => t (k, [a]) -> [(k, [a])]
fun = toList . foldr go empty
where
go (k, x) acc = alter (inc x) k acc
inc x Nothing = Just x
inc x (Just y) = Just (x ++ y)
无论列表是否排序,都可以正常工作:
\> fun [(1,[2]), (1,[3]), (1,[4]), (2,[1]), (2,[2]), (3,[1])]
[(1,[2,3,4]),(2,[1,2]),(3,[1])]
\> fun [(3,[1]), (1,[2]), (2,[1]), (1,[3]), (2,[2]), (1,[4])]
[(1,[2,3,4]),(2,[1,2]),(3,[1])]