(def nextStudentNumber 1000)
(defn s
[lastName firstName]
(let [student {:nextStudentNumber {:lastName lastName
:firstName firstName
:grades {}
:id (inc nextStudentNumber)}}]
In this instance, I have created the var nextStudentNumber
and I want to have map that keys that change on the of the student.
Yes, like any object a var can be a key in a map:
{#'foo 42}
=> {#'user/foo 42}
Since that won't help you, here is an example construct for your question:
(->> [{:name "Mara"} {:name "Lara"} {:name "Clara"}]
(map-indexed #(assoc %2 :id %1)) ;; assign an ID to each student
(group-by :id)) ;; Create a hash-map where students can be looked up by ID
=> {0 [{:name "Mara, :id 0}], 1 [{:name "Lara", :id 1}]
2 [{:name "Clara", :id 2}]}
Notice that this example is a bit redundant (as index lookup can be performed in a vector directly by invoking it with the index).
However, it shows the basic idea of working with immutable data: In independent steps you generate IDs and do grouping. You can compose such steps in many ways, like generating IDs for new students, adding them to a collection of existing students and grouping them by ID. Notice that the (group-by :id)
step works independently of how the IDs are generated and tolerates scenarios where two students have the same ID which you can then detect and handle.
The imperative approach is very untypical in functional Clojure world. If you have many functions that allocate students (and in your thinking should invoke s
), rather make them pure and let them return collections of students and give them names like load-students-from-file
, generate-ids
and use constructs like concat
, into
to combine collections returned by them without the need to mess with state, until you do I/O with the student data.
If, out of curiosity, you still want to mess with state, here is another (rather unidiomatic) code-sample:
(def student-count (atom 0))
(def students-by-id (atom {}))
(defn add-student! [first-name last-name]
(let [next-id (swap! student-count inc)]
(swap! students-by-id
assoc next-id {:first-name first-name, :last-name last-name
:id next-id})))
E. g.:
(add-student! "Mara" "Mad")
(add-student! "Lara" "Light")
(@students-by-id 1)
=> {:first-name "Lara", :last-name "Light", :id 1}
As you can see, everything is complected in one step here.
{:nextStudentNumber {:lastName lastName
:firstName firstName
:grades {}
:id (inc nextStudentNumber)}
I assume you want this transformed into something like:
{:1000 {:lastName lastName
:firstName firstName
:grades {}
:id (inc nextStudentNumber)}
in which case you'll want:
{(keyword (str nextStudentNumber)) {:lastName lastName
:firstName firstName
:grades {}
:id (inc nextStudentNumber)}
There are some other smelly things like camel case and inc
. We use levitating snake in clojure so lastName
would be last-name
. I don't know what you're doing with inc
but it's giving off heavy imperative vibes.