Search code examples
clojuremapsseq

Clojure - return new sequence from existing with side effect


I have an issue that I've run into and not sure how to approach. Basically, I have a database call which returns a sequence of maps. I need to apply a change to a specific element of each map, and then get back a new sequence with all the same values except the one I've changed.

I currently have the following, which iterates through the sequence and prints it, but I'm not sure how to actually build the new one.

(defn find-character-by-id []
  (let [
      character (mc/find-maps db "characters" {:user_id "5b4403d3c1025107593fa0b4" })
    ]
    (doseq [s character]
      (println s))))

an example map might be:

{:_id #object[org.bson.types.ObjectId 0x74bb0de6 5b467e5bc102511a1729a0f1], :user_id 5b4403d3c1025107593fa0b4, :value 20}

My goal right now is to be able to apply a custom function to cover the _id to a string, but I might also want to add an arbitrary amount to value so something generic is best.


Solution

  • If you wanted to convert each :_id to a string using some to-str function, you could write:

    (mapv ; Turn characters into a modified vector
      #(update % :_id to-str) ; by updating each map :_id using to-str
      characters)
    

    I'm mapping over each map, then updateing the :_id of each by applying to-str to each :_id val.

    Any time you want to turn one list into another list of an equal length, use map or mapv. You can also use for if you need some filtering capability.

    Any time you want to change a value associated with a key in a map based on its old value, use update.

    I'm just combining the two here.

    doseq should really only be used when you're iterating without the intent of creating a new list, or the new list you're creating is being created via some mutation. It doesn't evaluate to anything useful.