如何将列表处理/汇总到“不同”列表中

时间:2015-02-10 08:57:10

标签: haskell fold

我认为我需要像折叠或者折叠的东西,但我见过的例子似乎只是将列表压缩成一个简单的标量值。

我需要记住并重新使用列表中前一行的值(基本上是“分组依据”操作)

如果我的输入数据如下:     [["order1", "item1"],["", "item2"],["","item3"],["order2","item4"]]

最终采用以下方法的正确方法是什么:

[["order1",["item1","item2","item3"]],["order2",["item4"]]

data Order = Order { id :: Text, items :: [OrderItem]}

如果我想要一个稍微不同的结构怎么办?

[("order1",["item1","item2","item3"]),("order",["item4"])]

data OrderTuple = OrderTuple { order :: Order, items :: [OrderItem]}

如果我还想从OrderItem保持一些数值的运行总结怎么办?

编辑:这是我试图根据Frerich的答案开始工作的代码

--testGroupBy :: [[String]] -> [[String]]
testGroupBy :: [[String]] -> [(String, [String])]
testGroupBy z = 
  --groupBy (\(x:xs) (y:ys) -> x == y || null y) z
  groupBy testFunc z

testFunc :: [String] -> [String] -> Bool
testFunc (x:xs) (y:ys) = x == y || null y

2 个答案:

答案 0 :(得分:2)

模式匹配在这里很有用

groupData = foldl acc []
            where acc ((r, rs):rss) ("":xs)   = (r, rs ++ xs): rss
                  acc rss           (x:xs)    = (x, xs): rss
                  acc _             _         = error "Bad input data"

结果组的顺序相反,如果需要,请使用reverse

如果我想要一个稍微不同的结构怎么办?

只需将一个变换为另一个,您可以在groupData内部或作为单独的函数进行。

如果您接受没有fst元素的初始群组

groupData = foldr acc []
            where acc (x:xs)   []             = [(x, xs)]
                  acc ("":xs)  (("", rs):rss) = ("", rs ++ xs): rss
                  acc (x:xs)   (("", rs):rss) = (x, rs ++ xs): rss
                  acc (x:xs)   rss            = (x, xs): rss

然后

let xs = [["", "item8"],["", "item9"],["order1", "item1"],["", "item2"],["","item3"],["order2","item4"]]
print $ groupData xs

[("",["item9","item8"])
,("order1",["item3","item2","item1"])
,("order2",["item4"])]

答案 1 :(得分:1)

我首先尝试查看是否可以将函数定义为更高级函数的组合(例如fold),而不是寻找基于map的解决方案。让我开火ghci会话并玩abit:

λ: let x = [["order1", "item1"],["", "item2"],["","item3"],["order2","item4"]]

您的“分组依据”操作实际上有一个现有名称:Data.List.groupBy - 这几乎可以满足我们的需求:

λ: import Data.List
λ: let x' = groupBy (\(x:xs) (y:ys) -> x == y || null y) x
λ: x'
[[["order1","item1"],["","item2"],["","item3"]],[["order2","item4"]]]

groupBy应用程序将x中的所有元素放入第一个元素相等的一个组(即列表)中,或者第二个元素为空。然后可以按照您想要的格式进行按摩(在这种情况下,您使用map建议的第二个):

λ: let x'' = map (\x -> (head (head x), map (!! 1) x)) x'
λ: x''
[("order1",["item1","item2","item3"]),("order2",["item4"])]

全部放在一起:

groupData :: [[String]] -> [(String, [String])]
groupData = map (\x -> (head (head x), map (!! 1) x))
          . groupBy (\(x:xs) (y:ys) -> x == y || y == "")

我认为通过这种方式,构建一个合适的数据结构(即比嵌套列表更安全的类型)应该是直截了当的。