Joseph Cho
Joseph Cho

Reputation: 4214

Updating a nested record with new data in elm

I have two pieces of JSON that I've successfully decoded sequentially. I would like to take the new html_fragment and update my existing html_fragment. Generally this would be simple but my data structure is giving me difficulties:

type PostDataContainer
    = PostDataContainer PostData


type alias PostData =
    { title : String
    , comments : List Comment
    }


type alias Comment =
    { comment_id : Int
    , html_fragment : String
    }


type alias CommentHtml =
    { id : Int
    , html_fragment : String
    }

I've just gotten CommentHtml and would like to update the existing html_fragment in Comment. This is what I have so far:

    MergeCommentHtml commentHtmlData ->
        case commentHtmlData of
            Err err ->
                Debug.log ("Error decoding CommentHtmlData" ++ toString err)
                    ( mdl, Cmd.none )

            Ok commentHtml ->
                case mdl.maybePostDataContainer of
                    Just (PostDataContainer postData) ->
                        let
                            updatedCommentData = -- I dont know how to calculate this?
                        in
                        ( { mdl | postData = { postData | comments = updatedCommentData } }, Cmd.none )

Note that commentHtml here is a List CommentHtml. Any thoughts on how to update my old comment.html_fragment with the new values in commentHtml?

Upvotes: 1

Views: 344

Answers (2)

glennsl
glennsl

Reputation: 29106

Given that commentHtmlData is a List according to a comment, I think the easiest approach is to convert it to a Dict keyed by id, then map over the existing comments looking for the comment_id in the dict. If it exists, replace html_fragment, if not then return the original unmodified:

let
    commentHtmlDict =
        commentHtmlData
            |> List.map (\c -> (c.id, c))
            |> Dict.fromList

    updatedCommentData =
        postData.comments
            |> List.map (\comment ->
                case Dict.get comment.comment_id commentHtmlDict of
                    Just commentHtml ->
                        { comment | html_fragment = commentHtml.html_fragment }

                    Nothing ->
                        comment
            )

Upvotes: 1

Mark Bolusmjak
Mark Bolusmjak

Reputation: 24399

Option 1: just decode the data as it stands. When it's time to display it, arrange it appropriately via some function you write like rawJsonDataToNicerData.

Option 2:
Suppose you implement the following function:

-- given a new comment, and some PostData, return the new version of the PostData
updateData : CommentHtml -> PostData -> PostData

-- so now, assuming we can decode a CommentHtml with commentHtmlDeocder
-- we can do the following
dataUpdaterDecoder : Decoder (PostData -> PostData)
dataUpdaterDecoder 
   commentHtmlDecoder |> Decoder.andThen (\commentHtml -> updateData commentHtml)

Now wherever we were going to decode a commentHtmlDeocder we can decode a dataUpdaterDecoder instead, and use a bunch of these to update our data.

Here is an example of a relational data decoder in action using the idea above:

https://ellie-app.com/3KWmyJmMrDsa1

Upvotes: 2

Related Questions