Reputation: 52540
Is there a standard way to do the following?
{ model | country =
{ model.country | state =
{ model.country.state | city =
{ model.country.state.city | people =
model.country.state.city.people ++ [ newPerson ]
}
}
}
}
Of course, country
, state
, and city
are records nested in the model
. I just want to add a person in the nested city
record.
The above doesn't actually work. I get the following error on the first mention of model.country
:
I am looking for one of the following things:
"'" "|" an equals sign '=' more letters in this name whitespace
The way I was able to get this to work was to simply call a function at each step:
{ model | country = updateCountry newPerson model.country }
updateCountry person country =
{ country | state = updateState newPerson country.state }
And then the same for updateState
and updateCity
...
Upvotes: 2
Views: 572
Reputation: 6797
As of today(0.18.0
), arbitrary expressions are not allowed in record update syntax.
In other words, you can not access field of a record during the update:
model = { model.topProperty | childProperty = "Hello" }
^^^^^^^^^^^^^^^^^
Syntax error
It is a planned feature for Elm Compiler, but for now, you should consider restructuring your model or using one of the verbose workarounds.
Personally, I prefer let..in
expression for that, but I never use records with depth, higher than 3.
let..in
exampleIt looks super-verbose, but there is nothing bad with this approach. You will be abe to refactor it when Elm Compiler will support a better syntax.
Use this as a starting point for developing a set of helper functions for updates of different levels.
let
-- Deconstruct on every level.
{ model } = init
{ country } = model
{ state } = country
{ city } = state
{ people } = city
in
{ init
| model =
{ model
| country =
{ country
| state =
{ state
| city =
{ city
| people = "John" :: people
}
}
}
}
}
Upvotes: 3
Reputation: 14101
It is usually better to make the record structure flatter if you want to do stuff like this.
Typically:
What I tend to do is store everything at root level, and include IDs for reference. For your example this would look something like this:
type alias Model =
{ persons : Dict PersonID Person
, cities : Dict CityID City
, states : Dict StateID State
, countries : Dict CountryID Country
}
This structure allows you full access from root level to all entities. So adding a person would be:
{ model
| persons =
model.persons
|> Dict.insert newPersonID newPerson
}
To get all cities in a country would be a bit more work now BTW:
citiesInCountry : Model -> CountryID -> Dict CityID City
citiesInCountry model countryID =
let
statesInCountry =
model.states
|> Dict.filter (\id state -> state.countryID == countryID)
in
model.cities
|> Dict.filter (\id city -> Dict.member city.stateID statesInCountry)
So it kind of depends which operation is more frequent in your app:
Upvotes: 1
Reputation: 21005
There is https://github.com/evancz/focus but note the small print. Could you work with a different data structure instead, such as
Dict ( String, String, String ) Int
Then
addOne country state city =
Dict.update (country, state, city)
(\v -> case v of
Just v_ -> v_ + 1
Nothing -> 1)
database
Upvotes: 0