variablesdictionaryclojure

Clojure idiomatic solution to return variables as map


I am currently learning Clojure and I very much like it. However I come from an emacs-lisp background and I am still a bit confused when it comes to destructuring.

Currently I receive a database-query with a lot of keys. I break them down in different variables via destructuring, and finally, after creating a couple of new values via let, I reconstruct a hash-map as a return value.

The code looks like this:

(fn [{:keys [metre _id num filedate divtype section sandhi lines grammar devanagari filename notes trans sectionnumber fileauthor lang]}]
 (cond
   ;;when we have Chinese
   (= lang "zh")
    (let [newgrammar (apply str (map (fn [{:keys [word position line tag] }]
                                       (str "<span class=\"dropt\">" word
                                            "<span style=\"width:500px;\"><font color=\"green\">Word:</font>" word
                                            "<br><font color=\"red\">Function:</font> " tag
                                            "<br><font color=\"blue\">Meaning:</font> " (db/get-chindic-meaning word tag)
                                            "</span></span>")) grammar)) 
          newtrans (first (map #(last (last %)) trans))]              
      (hash-map :metre metre :id _id :num num :filedate filedate :divtype divtype :section section :sandhi sandhi :lines lines :grammar newgrammar :devanagari devanagari :filename filename :notes notes :trans newtrans :sectionnumber sectionnumber :fileauthor fileauthor :lang lang))

This is just one extract, there are many different conditions following. So there is a lot of code which feels totally unnecessary to me.

I guess this is ugly and certainly not the way it should be. Any suggestions on how to do this right? Any help would be deeply appreciated!


Solution

  • you can capture the whole map this way:

    user> (defn my-fun [{:keys [a b c d e] :as params-map}]
        (assoc params-map
               :a 200
               :b 100))
    #'user/my-fun
    user> (my-fun {:a 1 :b 2 :c 3 :d 4 :e 5})
    {:a 200, :b 100, :c 3, :d 4, :e 5}
    

    so you just update some specific values in a map, without redefining it. also take a look at update, it might be useful in this case

    as for me, I would rewrite your fn like this: (move out some functions and use update)

    (defn update-grammar [grammar]
      (apply str (map (fn [{:keys [word position line tag] }]
                        (str "<span class=\"dropt\">" word
                             "<span style=\"width:500px;\"><font color=\"green\">Word:</font>" word
                             "<br><font color=\"red\">Function:</font> " tag
                             "<br><font color=\"blue\">Meaning:</font> "     (db/get-chindic-meaning word tag)
                             "</span></span>")) grammar)))
    
    (defn update-trans [trans] (first (map #(last (last %)) trans)))
    
    (fn [{:keys [metre _id num filedate divtype section 
                 sandhi lines grammar devanagari filename 
                 notes trans sectionnumber fileauthor lang]
          :as params-map}]
     (cond
       ;;when we have chinese
       (= lang "zh")
       (-> params-map
           (update :grammar update-grammar)
           (update :trans update-trans))
    

    you see, now you can even remove grammar and trans from :keys vector, as you don't need them anymore