2016-05-03 3 views
2

私はElmプログラムで結合したい2つのデータ構造を持っています。そのタイプはList (a, List b)List cです。これはモデルにデータを格納する最も簡単な方法ですが、表示する前にビューモデルに変換する関数を記述したいと思います。奇妙な形のリストをエルムでどのように圧縮できますか?

type alias Model1 a b = List (a, List b) 
type alias Model2 c = List c 

type alias ViewModel a b c = List (a, List (b, c)) 

toViewModel : Model1 -> Model2 -> ViewModel 
toViewModel model1 model2 = 
    ??? 
toViewModel

関数はMODEL2のサイズはモデル1

におけるサブリストのサイズの合計と同じであると仮定2.モデルの要素とモデル1でサブリストを圧縮すべきです

たとえば、model1の長さが2で、2つの要素のサブリストの長さが3と4の場合、モデル2の長さは7になります。モデル1の最初の要素のサブリストは、モデル2の最初の3要素、 1の2番目の要素のサブリストは、モデル2の次の4つの要素で圧縮されている必要があります。

次の例は、 toViewModel機能を構築することができますか

Diagram of example drawn on paper

答えて

3

エルムが内蔵されzip機能はありませんが、それはList.map2の面でそれを定義するのは簡単です:

zip : List a -> List b -> List (a, b) 
zip = List.map2 (,) 

あなたは、それ自体でバラバラリストにそれを使用することはできませんが、モデル2の最初のnアイテムを使用してModel1のサブリストを圧縮することができます。ジップアップする必要があるもののリストを取得するには、ジップ部分にはList.take、次のModel1エンティティで圧縮する必要がある残りの項目はModel2から取得するためにList.dropを使用する必要があります。これは、次のような関数を使って行うことができます。 zipを使用すると、どちらのリストで停止しようとしていること

toViewModel : Model1 a b -> Model2 c -> ViewModel a b c 
toViewModel m1 m2 = 
    case m1 of 
    [] -> 
     [] 

    ((m1ID, m1List)::rest) -> 
     let 
     len = List.length m1List 
     init' = List.take len m2 
     tail' = List.drop len m2 
     in 
     (m1ID, zip m1List init') :: toViewModel rest tail' 

通知は最初に終わるので、あなたが不均一長さのリストを持っている場合、アイテムがドロップを取得しようとしています。

編集からここに明示的な再帰が主な機能は、マッピング

を介して第1のグループにモデル2になり、これを解決するための別の方法を実現することができるような方法で分離され、これを行うための追加的な方法です各リストには、同等のModel1エントリの適切な数の要素が含まれています。

ここでは、リストを複数の小さなリストに分割する関数takesを定義します。これらのリストのサイズは、最初のパラメータとして渡されます。

takes : List Int -> List a -> List (List a) 
takes counts list = 
    case counts of 
    [] -> [] 
    (x::xs) -> List.take x list :: takes xs (List.drop x list) 

今あなたが内部のリストを圧縮することができますので、あなたはMODEL1入力に対してそれをマッピングすることができ、グループにモデル2リストをtakes機能を使用することができます。ここで

toViewModel : Model1 a b -> Model2 c -> ViewModel a b c 
toViewModel model1 model2 = 
    let 
    m2Grouped = takes (List.map (List.length << snd) model1) model2 
    mapper (m1ID, m1List) m2List = (m1ID, zip m1List m2List) 
    in 
    List.map2 mapper model1 m2Grouped 
+0

次の例を考えてみてください。実際のコードでは、具体的な型ですので、コードを一般化すると間違えてしまいました。 – Eric

+0

私は私の答えに論理エラーを修正し、現在廃止されたタイプのエイリアスの推奨事項を削除しました –

+0

答えは正確で一般化されたサマリーですか? "奇妙な形のリストを組み合わせるには、' zip = List.map2(、) 'はおそらく十分な柔軟性がないので、パターンマッチングと再帰を使用してください。" – Eric

1

少し仕事を取るsimplierです:両方のリスト からのデータでList (a, b, c)を取得するには、Model2でそれをマージModel1

  • からフラットList (a, b)を作成するために使用List.concatMap

    1. リストをグループ化することによってaを取得して最終的にList (a, List (b, c))List.filterMap

    私はここでテストを実行するためにいくつかのデータを模擬しなければなりませんでした。私は、型の別名を修正した

    import Graphics.Element exposing (show) 
    
    
    -- List (a, List b) 
    type alias Model1 = List (String, List Int) 
    
    
    -- List c 
    type alias Model2 = List Bool 
    
    
    -- List (a, List (b, c)) 
    type alias ViewModel = List (String, List (Int, Bool)) 
    
    
    toViewModel : Model1 -> Model2 -> ViewModel 
    toViewModel model1 model2 = 
        let 
        -- Map merge both lists and map values to the grouping key. 
        mappedList = 
         model1 
         |> List.concatMap (\(groupBy, list) -> List.map (\el -> (groupBy, el)) list) 
         |> List.map2 (\val2 (groupBy, val1) -> (groupBy, (val1, val2))) model2 
    
        -- Extract a list for for specified grouping key. 
        getListByKey search = 
         List.filterMap 
         (\(key, val) -> if key == search then Just val else Nothing) 
         mappedList 
    
        in 
        List.map (\(search,_) -> (search, getListByKey search)) model1 
    
    
    initModel1 : Model1 
    initModel1 = 
        [ ("foo", [ 1, 2, 3 ]) 
        , ("bar", [ 4, 5, 6, 7]) 
        ] 
    
    
    initModel2 : Model2 
    initModel2 = 
        [ True 
        , False 
        , True 
        , False 
        , True 
        , False 
        , True 
        ] 
    
    main = 
        toViewModel initModel1 initModel2 
        |> show 
    
  • 関連する問題