Thanks for that, Mike. Like I've written earlier in this thread, I've been trying a few approaches and have been wondering what others have come up with. (I don't need to reinvent the wheel.)
I hadn't quite gotten as far as you've described, though I was definitely on that path with one approach. Another I've tinkered with is using clojure.walk, but I dislike that it touches every node. Expensive on a large data set. Zippers don't make me happy either. Definitely agree with keeping data as flat as possible, but even so you can end up with a decent tree shape, which the data structures I'll be consuming and generating actually have. In my app, a Product is a Component (not a React/Reagent/Om component) that has many Components. Each Component can have many Components or many Substances. Even though the components are stored flat, they form a logical tree of n-levels of Components. Components and Substances can have other lists as well. So, you can see that my needs to traverse into a rich data structure are immediate, even with flattened storage of the Components in a single list on the root Product. Interestingly, last Summer I started on this with Reagent and later Om using the unflattened data (why?--it was flat in the db!). It got turned into an app for different data that was not recursive in any way, but I'd still solved that problem. I turned to Om then b/c it has cursors and they solved this problem so well that I felt it was worth the other pains involved in using Om, so I gave up on Reagent. In a nutshell: Reagent made developing React-style components really simple and easy, but dealing with a rich app-data atom was such a PITA, that I switched to Om whose cursors made that easy while making component development much more cumbersome. Since seeing and playing with Re-Frame, I've decided to do this next project with re-frame. Anyway, thanks again for the info, as it definitely helps me along. Jamie On Mar 25, 2015, at 9:46 PM, Mike Thompson <[email protected]> wrote: > On Thursday, March 26, 2015 at 2:08:23 AM UTC+11, Jamie Orchard-Hays wrote: >> In Om, I didn't have to think about how to find the data in app-db to update >> as Om has cursors. In the cursorless world of re-frame, I am wondering what >> are favorite strategies for updating data deep inside a decently rich app-db >> tree. >> >> ie, say I want to update a field that is in a map that is in a list in >> app-db. I want to update the :name value in one of the maps in :some-list: >> >> (def app-db (atom >> {:some-list >> [{:id "asdfadfa" :name "Foo Bar" ...} >> {:id "gfdsalkjc" :name "Boo Baz" ...} >> ....]})) >> >> This isn't so difficult to approach, but what about a field in a map in a >> list in a map in a list... You get the idea. >> > > > Tim Peters guides us in this area by recommending: "Flat is better than > nested". > https://www.python.org/dev/peps/pep-0020/ > > But that isn't always possible, I know, so to answer your question instead of > providing gratuitous quotations ... > > As much as possible, I try to conceptualise "app-db" to be a database. That's > the reason for that "db" in the name. > > And, if you wanted to "update" a database, you need: > - the unique key of the thing to be updated > - the name of the table (collection). > > Generally "path middleware" nicely handles the "name of the table" part of > this pair. It gets your handler to the "root" of the data structure (table? > collection?). > > In the situation you are describing, the "unique key" bit is some sort of > "path" into a data structure. There are layers of vectors and maps to > traverse, even if middleware gets you to the base of this data structure you > want to update. > > In such a case, you'll have to "build the unique key" as you descend. A > read-only cursor-like process. > > So, let's imagine you represent your path as a vector (of keys or indexes), > then in each level you would pass down this path vector, conj-ed with a new > part of the id, incrementally building the unique key as you descend, and > passing it down. > > Then, at the leaves, when you "dispatch", you can include the path vector in > the event payload. The handler then has the information it needs to perform > the update. > > This deep-tree situation is not a common usecase for me, but if you ran into > this situation a lot, then I'd think about adapting the reagent cursors for > the job. > > In summary: when you dispatch, pass through the "unique key" (path), of the > thing you want updated. Your handler may additionally have "path > middleware" on it, which resolves the “name of the table” part of any update. > So you get the job done via that combination. > > A super simple version of this, can be seen here: > https://github.com/Day8/re-frame/blob/46b8848f3bab432832a4517f0cccf36cb4197f65/examples/todomvc/src/todomvc/handlers.cljs#L54-L58 > > "todo-ware" contains path middleware which gets the handler directly to the > "todos table" (see the parameter "todos"), and the "id" in the event is the > unique key required for the update. In your case, you'd just have a more > complicated "id" (it's a vector created in a read-only cursor-like fashion). > > I worry that I've simply told you what you already know here. > > > -- > Mike > > > -- > Note that posts from new members are moderated - please be patient with your > first post. > --- > You received this message because you are subscribed to the Google Groups > "ClojureScript" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To post to this group, send email to [email protected]. > Visit this group at http://groups.google.com/group/clojurescript. -- Note that posts from new members are moderated - please be patient with your first post. --- You received this message because you are subscribed to the Google Groups "ClojureScript" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at http://groups.google.com/group/clojurescript.
