如何在Elm中将一个列表分为四个列表?

Ana*_*nth 5 functional-programming elm

我有一个必须渲染的项目列表。我有一个函数viewItem可以渲染一个项目。我做的很简单List.map viewItem items,现在我有一个可以显示的项目列表。

我的观点有四列。如何将此列表分为四个列表,其中包含原始列表中的所有元素?

这就是我现在的做法,但是我必须缺少一些东西。我希望能够将其拆分成五列甚至六,而无需编写col4 = ...col5 = ...每一次。

splitColumns : Int -> Array a -> Array (List a)
splitColumns cnum xs =
    let
        ixdList =
            Array.toIndexedList xs
    in
    List.filterMap
        (\a ->
            if modBy 4 (Tuple.first a) == cnum then
                Just (Tuple.second a)

            else
                Nothing
        )
        ixdList


viewItems : Array Item -> Html msg
viewItems items =
    let
        itemsHtml =
            Array.map viewItem items

        col0 =
            splitColumns 0 itemsHtml

        col1 =
            splitColumns 1 itemsHtml

        col2 =
            splitColumns 2 itemsHtml

        col3 =
            splitColumns 3 itemsHtml
    in
    main_
        [ class "section" ]
        [ Html.div
            [ class "container" ]
            [ Html.div
                [ class "columns" ]
                [ Html.div
                    [ class "column" ]
                    col0
                , Html.div
                    [ class "column" ]
                    col1
                , Html.div
                    [ class "column" ]
                    col2
                , Html.div
                    [ class "column" ]
                    col3
                ]
            ]
        ]
Run Code Online (Sandbox Code Playgroud)

O.O*_*nce 5

您可以List.map与 一起使用List.range。生成从到(含)List.range a b的整数列表。ab

您的viewItems功能大大简化了:

viewItems : Array Item -> Html msg
viewItems items =
    main_
        [ class "section" ]
        [ Html.div
            [ class "container" ]
            [ Html.div
                [ class "columns" ]
                List.map (\n -> Html.div [ class "column" ] (splitColumns n (Array.map viewItem items)) (List.range 0 3)
            ]
        ]
Run Code Online (Sandbox Code Playgroud)

如果你想支持不同数量的列,你当然可以用参数替换硬4编码。splitColumns


gle*_*nsl 5

您可以将当前方法重写为仅一次通过这样的折叠:

cols : List a -> { col0 : List a, col1 : List a, col2 : List a, col3 : List a }
cols list =
    list
        |> List.foldl
            (\x ( i, cols ) ->
                case modBy 4 i of
                    0 ->
                        ( i + 1, { cols | col0 = x :: cols.col0 } )

                    1 ->
                        ( i + 1, { cols | col1 = x :: cols.col1 } )

                    2 ->
                        ( i + 1, { cols | col2 = x :: cols.col2 } )

                    3 ->
                        ( i + 1, { cols | col3 = x :: cols.col3 } )

                    _ ->
                        ( i + 1, cols )
            )
            ( 0, { col0 = [], col1 = [], col2 = [], col3 = [] } )
        |> Tuple.second
Run Code Online (Sandbox Code Playgroud)

这也可以在内部跟踪索引,因此不需要您为其提供索引列表,但是仍然为四列进行了硬编码。如果我们希望能够将其与任意数量的列一起使用,则必须使用可以按顺序容纳任意数量的项目的数据结构。数组非常适合此操作,允许我们使用使用modBy以下命令计算的索引来更新它:

cols : Int -> List a -> List (List a)
cols n list =
    list
        |> List.foldl
            (\x ( i, cols ) ->
                let
                    index =
                        modBy n i

                    tail =
                        cols |> Array.get index |> Maybe.withDefault []
                in
                ( i + 1, Array.set index (x :: tail) cols )
            )
            ( 0, Array.repeat n [] )
        |> Tuple.second
        |> Array.toList
Run Code Online (Sandbox Code Playgroud)

然后List.map,我们可以在view函数中使用它们来渲染它们:

viewItems : Array Item -> Html msg
viewItems items =
    let
        itemsHtml =
            Array.map viewItem items
                |> Array.toList
    in
    main_
        [ class "section" ]
        [ Html.div
            [ class "container" ]
            [ Html.div
                [ class "columns" ]
                (cols 4 itemsHtml |> List.map (Html.div [ class "column" ]))
            ]
        ]
Run Code Online (Sandbox Code Playgroud)