elm脚本从后端代码获取csv字符串csv
。我要创建一个表,并且每个单元格的类名称应为列名称。
可以从rows = String.split "\n" csv
获得表的行。如果不需要定义类,可以通过在List.map
上应用双重嵌套的rows
来创建表。但是为了给每个单元格分配类,我需要保存row的第一个元素,该元素包含列(或类)名称,并将其与其余各行配对。由于榆木中没有for循环,因此我的策略是定义一个递归函数以创建所有tr
元素,并使用tr
连接List.map
元素。此函数的输入是第一行names
和另一行cols
。它们都是List String
。每次运行时,它使用两个列表的头(来自td
)创建一个List.head
,然后传递两个列表(来自List.drop
)的其余元素进行递归。但是我无法连接两个返回的组件。
createRow names cols =
case cols of
[] -> text ""
_ ->
let
c0 = (List.head cols)
c = (List.drop 1 cols)
n0 = (List.head names)
n = (List.drop 1 names)
in
td [] [text <| toString <| c0]
, createRow n c
我应该在in
块中放什么?还是有更好的方法实现我的目的?
答案 0 :(得分:4)
首先,我建议您不要手动解析CSV。包管理器上有多个CSV解析器,因此我们可以专注于处理这些值。
https://package.elm-lang.org/packages/periodic/elm-csv/latest/Csv
是一个选项,但它们在解析后都会为您提供Csv
类型,如下所示:
type alias Csv =
{ headers : List String
, records : List (List String)
}
正如您所触摸的,标题与每行中的值一样多(否则CSV无效)。通过递归进行删除/修饰可以完成工作,但是我们可以依靠列表操作来完成大部分工作,从而更具声明性:
classTable : Csv -> Html msg
classTable csv =
table []
(csv.records |> List.map (tableRow csv.headers))
tableRow : List String -> List String -> Html msg
tableRow headers values =
let
insertNextCellInRow ( header, value ) row =
td [ class header ] [ text value ] :: row
in
tr []
(List.map2 Tuple.pair headers values
|> List.foldr insertNextCellInRow []
)
注意:List.foldr
是递归的,并以命令式语言达到循环的目的(“对于此列表中的每个项目,请应用此功能并在此其他列表中收集结果”)。但是,它与传递给它的值的类型无关,并且使我们能够专注于转换值。
答案 1 :(得分:3)
在递归函数中,您需要传递(部分计算的)结果,以便您可以在每次调用中对其进行修改,并在完成递归后将其返回。这样看起来可能像这样:
createRow : List String -> List String -> List (Html msg) -> List (Html msg)
createRow names cols cells =
case cols of
[] -> cells
col :: remainingCols ->
let
name =
names
|> List.head
|> Maybe.withDefault ""
remainingNames = List.drop 1 names
cell =
td [class name] [text col]
in
createRow remainingNames remaningCols (cells ++ [ cell ])
此处还有其他一些值得注意的变化:
cols
列表的开头(也称为结尾)List.head
返回一个Maybe a
(在这种情况下,a
是String
),所以我向Maybe.withDefault
添加了一个呼叫cols
是List String
,因此您无需调用toString
,就可以直接将其传递给text
[]
参数传递cells