What is the best way in Clojure (actually ClojureScript) to accomplish the following:
xs
, each of type T
(T
is a map, if we want to be specific)x
, which alternates between various xs
or a "none selected" state. The number of selected items is either 1 or zero.xs
and x
will have event listeners listening to them: some listen for any change in xs
; some listen to any change in x
. (That is, they listen for both an update in the state of the selected item, as well as a switching of which item is selected.) (I'm actually using the Reagent wrapper for React, so this affects when components will update.)x
(don't need to edit non-selected xs
, though need to be able to add new xs
)xs
, a method to select it. (Including the case of select "none")Possibilities I've thought of so far:
xs
an atom which is a vector of T
s, and make sure every T
element knows its own index. Replace x
with x_idx
which stores the index of the selected item (or nil
). Selecting an element just means getting its index and changing x_idx
to that index, both of which are constant-time ops. Downsides to this are that it makes the structure a little less elegant and simple: I'm always passing around the index of the selected item, and every operation I want to do has to work with the index instead of the item itself, as it would like to. Since my T
objects are small, it would be more pleasing to just have a "selected object" variable which is of type T
.xs
an atom vector and x
an atom which stores a T
. Now x
is a T
, which is nice, but when I want to update info about x
, I have to make two calls to reset!
or swap!
: one for x
and one for the element in xs
which x
represents. Inelegant for obvious reasons. And I have to do these in quick succession or else there will be an inconsistency in the data: in between the two calls, the event listeners listening to xs
will see the selected item in one state, and the ones listening to x
will see it in another state.T
elements a field to tell if they're selected or not, and get rid of x
. This would be right if multiple items could be selected at once, but since only one item can be selected, it just makes me do a linear search every time I want the selected item, which sucks.The point of this question is not to solve some particular issue (any of the possibilities above work fine for the scope of the small project I'm working on), but to learn about Clojure data structures. This seems like a common enough situation that there would be some structure around it. Responses along the lines of "you should be trying to answer a different question entirely..." are welcome.
You (I) want to look into cursor
s and reaction
s, two of the more advanced features of Reagent atoms. They can be used to have a collection and a selected element inside it which is automatically updated. See here for example.
Suppose you have a r/atom
holding vector of objects and you want to have a selected object which can change, and which is directly editable. One way to achieve this is to keep an atom storing the index of the selected item and then
(defn my-get-set
([_k] (@vector-of-items @idx-of-selected-item))
([_k v] (reagent.core/rswap! my-state-vector assoc @idx-of-selected-item v)))
(def selected-item (reagent.core/cursor my-get-set []))
Edit: Changed reagent.core/cursor my-get-set nil
to reagent.core/cursor my-get-set []
. The former leads to problems.