Search code examples
data-structuresclojureclojurescriptreagent

Choice of data model: Nested map or vector for Reagent atom to be updated-in and mapped over?


I'm working on a Reagent App to manage seasons for a hotel-booking-site. The data is shown in a tabular view and is currently stored in an atom holding a nested map with season-ids as keys, like so:

(def seasons (r/atom
{1
 {:begin "2020-04-02"
  :end "2020-04-19"
  :selected false
  …much more data like price per room etc}
2
{:begin "2020-04-20"
  :end "2020-06-18"
  :selected true
  }
…}))

The main advantage of this structure is easy deleting/modifying of seasons from within my components via #(dissoc @seasons season-id) or (update-in seasons [season-id :begin] "2020-04-21"), which would be more cumbersome with the flatter alternative of integrating the id into the map representing a season, like so:

(def seasons
(r/atom
[{:id 1
  :begin "2020-04-02"
  :end "2020-04-19"
  :selected false
  …much more data like price per room etc}
 {:id 2
  :begin "2020-04-20"
  :end "2020-06-18"
  :selected true
  }…]))

This version would lend itself much better to being mapped over, which is not very pleasant with my current data model, where I have to always recombine the id with the actual map into a binary vector and then stick the resulting seq back into a map.

I'm more and more inclined to switch to this second model as the removal/update would be feasible by passing the entire season into a function… there would be no need for ids at all then, e.g.:

(vec (remove #(= % season) @seasons))

or, respectively:

(vec (map #(if (= % season) 
       (update-in % [:begin "2020-04-21"]) %) @seasons))

Since this is my first real-world Reagent app I'm a bit unsure as to which data model is actually preferable? Are there any performance considerations between both approaches? Which one would seem more sane to a seasoned Reagent developer? Is there a third way that I'm unaware of?

Many thanks for any input to this somewhat open question!

Screenshot


Solution

  • As you describe your purposes (seasons), performance is not a consideration. Three "seasons" per day is only ~1000 per year - a small number to a computer.

    So, which method is easier for your code? You have discussed two use-cases which come down favoring method A in one case and method B in another. It is up to you to decide.

    In method A, you have "pre-indexed" the data, so you can go directly to a thing and modify it. In method B, you have to do a mini-search to find a thing, before processing it. For a larger problem, method B eventually morphs into a DB like Postgres, Datomic, or Neo4J, etc.

    For small problems that stay in memory, you could use a library:

    These are probably overkill at this stage, but you may want to keep them in mind for the future.