Search code examples
clojure

How to add or update a list in clojure?


I've this function and I don't know how to implement it. I only know how to use concat, but it's wrong because it only adds and I also need to update.

Can anyone help me?

This is the function:

(defn update-env 
  [env-global key value]
  (if (and (list? value) (= (first value) '*error*))
    env-global
    (concat env-global (list key value))))

This is the code I need to run: (update-env '(+ add - sub x 1 y 2) 'x 3)

This is the result it should give: (+ add - sub x 3 y 2)

Thanks in advance!


Solution

  • the trick is to operate on collection's elements pairwise something alike the common lisp's plist. You can make up something like this:

    (defn plist-assoc [key new-val plist]
      (->> plist
           (partition 2)
           (mapcat (fn [[k v]] [k (if (= k key) new-val v)]))))
    
    user> (plist-assoc 'x 3 '(+ add - sub x 1 y 2))
    ;;=> (+ add - sub x 3 y 2)
    
    user> (plist-assoc 'z 3 '(+ add - sub x 1 y 2))
    ;;=> (+ add - sub x 1 y 2)
    

    so than your function looks like this:

    (defn update-env [env-global key value]
      (if (and (list? value) (= (first value) '*error*))
        env-global
        (plist-assoc key value env-global)))
    
    user> (update-env '(+ add - sub x 1 y 2) 'x 3)
    ;;=> (+ add - sub x 3 y 2)
    

    update if you need to update or insert new value, you can use something like this instead:

    (defn assoc-plist2 [key new-val plist]
      (let [[l r] (->> plist
                       (partition 2)
                       (split-with (comp nil? #{key} first)))]
        (apply concat `(~@l ~[key new-val] ~@(rest r)))))
    
    user> (assoc-plist2 'x 10 '(+ add - sub x 1 y 2))
    ;;=> (+ add - sub x 10 y 2)
    
    user> (assoc-plist2 'z 10 '(+ add - sub x 1 y 2))
    ;;=> (+ add - sub x 1 y 2 z 10)