How to memoize an api response in clojure? - clojure

I want to memoize a function return that a function makes a http request to an API.
I'm unable to do it.
(defn _get_userid
[id cid]
(p1.nms2/get_uerid id cid))
(def get_userid
(memo/ttl _get_userid
{}
:ttl/threshold p1.constant/ttl-millisecs))

Given your 2nd parameter is like a context (for logging), you can use a dynamic var so you don't need to pass it as an extra argument to your memoized function.
(def ^:dynamic *cid* nil)
(def get-userid
(memoize
(fn [id]
{:input id
:context *cid*
:output (inc id)})))
(binding [*cid* "what"]
(get-userid 1))
;; => {:input 1, :context "what", :output 2}
(binding [*cid* "when"]
(get-userid 1))
;; => {:input 1, :context "what", :output 2}
(binding [*cid* "why"]
(get-userid 2))
;; => {:input 2, :context "why", :output 3}

Related

How to group-by a collection that is already grouped by in Clojure?

I have a collection of maps
(def a '({:id 9345 :value 3 :type "orange"}
{:id 2945 :value 2 :type "orange"}
{:id 145 :value 3 :type "orange"}
{:id 2745 :value 6 :type "apple"}
{:id 2345 :value 6 :type "apple"}))
I want to group this first by value, followed by type.
My output should look like:
{
:orange [{
:value 3,
:id [9345, 145]
}, {
:value 2,
:id [2935]
}],
:apple [{
:value 6,
:id [2745, 2345]
}]
}
How would I do this in Clojure? Appreciate your answers.
Thanks!
Edit:
Here is what I had so far:
(defn by-type-key [data]
(group-by #(get % "type") data))
(reduce-kv
(fn [m k v] (assoc m k (reduce-kv
(fn [sm sk sv] (assoc sm sk (into [] (map #(:id %) sv))))
{}
(group-by :value (map #(dissoc % :type) v)))))
{}
(by-type-key a))
Output:
=> {"orange" {3 [9345 145], 2 [2945]}, "apple" {6 [2745 2345], 3 [125]}}
I just couldnt figure out how to proceed next...
Your requirements are a bit inconsistent (or rather irregular) - you use :type values as keywords in the result, but the rest of the keywords are carried through. Maybe that's what you must do to satisfy some external formats - otherwise you need to either use the same approach as with :type through, or add a new keyword to the result, like :group or :rows and keep the original keywords intact. I will assume the former approach for the moment (but see below, I will get to the shape as you want it,) so the final shape of data is like
{:orange
{:3 [9345 145],
:2 [2945]},
:apple
{:6 [2745 2345]}
}
There is more than one way of getting there, here's the gist of one:
(group-by (juxt :type :value) a)
The result:
{["orange" 3] [{:id 9345, :value 3, :type "orange"} {:id 145, :value 3, :type "orange"}],
["orange" 2] [{:id 2945, :value 2, :type "orange"}],
["apple" 6] [{:id 2745, :value 6, :type "apple"} {:id 2345, :value 6, :type "apple"}]}
Now all rows in your collection are grouped by the keys you need. From this, you can go and get the shape you want, say to get to the shape above you can do
(reduce
(fn [m [k v]]
(let [ks (map (comp keyword str) k)]
(assoc-in m ks
(map :id v))))
{}
(group-by (juxt :type :value) a))
The basic idea is to get the rows grouped by the key sequence (and that's what group-by and juxt do,) and then combine reduce and assoc-in or update-in to beat the result into place.
To get exactly the shape you described:
(reduce
(fn [m [k v]]
(let [type (keyword (first k))
value (second k)
ids (map :id v)]
(update-in m [type]
#(conj % {:value value :id ids}))))
{}
(group-by (juxt :type :value) a))
It's a bit noisy, and it might be harder to see the forest for the trees - that's why I simplified the shape, to highlight the main idea. The more regular your shapes are, the shorter and more regular your functions become - so if you have control over it, try to make it simpler for you.
I would do the transform in two stages (using reduce):
the first to collect the values
the second for formating
The following code solves your problem:
(def a '({:id 9345 :value 3 :type "orange"}
{:id 2945 :value 2 :type "orange"}
{:id 145 :value 3 :type "orange"}
{:id 2745 :value 6 :type "apple"}
{:id 2345 :value 6 :type "apple"}))
(defn standardise [m]
(->> m
;; first stage
(reduce (fn [out {:keys [type value id]}]
(update-in out [type value] (fnil #(conj % id) [])))
{})
;; second stage
(reduce-kv (fn [out k v]
(assoc out (keyword k)
(reduce-kv (fn [out value id]
(conj out {:value value
:id id}))
[]
v)))
{})))
(standardise a)
;; => {:orange [{:value 3, :id [9345 145]}
;; {:value 2, :id [2945]}],
;; :apple [{:value 6, :id [2745 2345]}]}
the output of the first stage is:
(reduce (fn [out {:keys [type value id]}]
(update-in out [type value] (fnil #(conj % id) [])))
{}
a)
;;=> {"orange" {3 [9345 145], 2 [2945]}, "apple" {6 [2745 2345]}}
You may wish to use the built-in function group-by. See http://clojuredocs.org/clojure.core/group-by

Update Clojure map that is wrapped inside an atom

I am trying to update a map that is inside an atom. Each map is referenced by a value.
(def a (atom {}))
(defn foo [id mps]
(let [x (merge (get mps id) mps)]
(swap! a assoc id x) x))
(foo 2 {:baz 88}) => {:baz 88}
#a => {2 {:baz 88}}
(foo 2 {:bar 99}) => {:bar 99} ??
#a => {2 {:bar 99}} ??
It appears to be overwriting the map instead of updating it. The result I am looking for is:
(foo 2 {:baz 88}) => {:baz 88}
#a => {2 {:baz 88}}
(foo 2 {:bar 99}) => {:bar 99, :baz 88}
#a => {2 {:bar 99, :baz 88}}
Any help would be great
you are replacing the old value with the new one (using assoc). what you are looking for, is the behaviour of merge-with (http://conj.io/store/v1/org.clojure/clojure/1.7.0-alpha4/clj/clojure.core/merge-with/) directly on the atom. Like:
user=> (def a (atom {}))
#'user/a
user=> (swap! a #(merge-with merge % {:a {:b 1}}))
{:a {:b 1}}
user=> (swap! a #(merge-with merge % {:a {:c 2}}))
{:a {:c 2, :b 1}}

how do I write a clojure factory function with an incrementing id?

I'd like to write something like this (not working):
(defn new-connection []
(let [c (atom 0)]
{:id #(swap! c inc)
:writebuf (ByteBuffer/allocate 8096)
:readbuf (ByteBuffer/allocate 8096)}))
Such that I can get incrementally id'd maps with (new-connection), Do I need to define the counter separately or is there a way to inline it with this function?
This is a very general problem of having a counter. If you decompose things a bit you will find that you need three things: 1) Something to create new counter 2) A connection counter 3) The new connection function which accepts the id as param rather than calling the counter itself as that makes this function more general.
(defn new-counter []
(partial apply swap! (atom 0) inc []))
(def connection-counter (new-counter))
(defn new-connection [id]
{:id id
:writebuf (ByteBuffer/allocate 8096)
:readbuf (ByteBuffer/allocate 8096)})
Now you can use it like:
(new-connection (connection-counter))
You can use closure mechanism to "inline" counter:
(let [c (atom 0)]
(defn new-connection []
{:id (swap! c inc)
:writebuf :ByteBuffer
:readbuf :ByteBuffer})
(defn get-counter []
#c))
(get-counter)
=> 0
(new-connection)
=> {:id 1, :writebuf :ByteBuffer, :readbuf :ByteBuffer}
(new-connection)
=> {:id 2, :writebuf :ByteBuffer, :readbuf :ByteBuffer}
(get-counter)
=> 2
Or if you need to control counter start value:
(defn create-connection-fn [init-counter-value]
(let [c (atom init-counter-value)]
(fn []
{:id (swap! c inc)
:writebuf :ByteBuffer
:readbuf :ByteBuffer})))
(def new-connection (create-connection-fn 10))
(new-connection)
=> {:id 11, :writebuf :ByteBuffer, :readbuf :ByteBuffer}
(new-connection)
=> {:id 12, :writebuf :ByteBuffer, :readbuf :ByteBuffer}
Edit. I suggest you to define the counter as separated variable if there is no reason to "hide" it.

How do I modify a :arglists to a Clojure fn or macro?

How do I modify the :arglist attribute for a clojure fn or macro?
(defn tripler ^{:arglists ([b])} [a] (* 3 a))
(defn ^{:arglists ([b])} quadrupler [a] (* 4 a))
% (meta #'tripler) =>
{:arglists ([a]), :ns #<Namespace silly.testing>, :name tripler, :line 1, :file "NO_SOURCE_PATH"}
% (meta #'quadrupler) =>
{:arglists ([a]), :ns #<Namespace silly.testing>, :name quadrupler, :line 1, :file "NO_SOURCE_PATH"}
Ok, no luck there, so I tried doing the following.
(def tripler
(with-meta trippler
(assoc (meta #'tripler) :arglists '([c]))))
% (with-meta #'tripler) =>
{:ns #<Namespace silly.testing>, :name tripler, :line 1, :file "NO_SOURCE_PATH"}
Hmm, so now the :arglists key is gone? Well, I give up, how do I do this? I would simply like to modify the value of :arglists. The examples above use defn, but I would also like to know how to set the :arglists using a macro (defmacro).
You don't need to do anything as ugly as the suggestions so far. If you take a look at defn's own arglists…
user=> (:arglists (meta #'clojure.core/defn))
([name doc-string? attr-map? [params*] prepost-map? body]
[name doc-string? attr-map? ([params*] prepost-map? body) + attr-map?])
You're looking for attr-map. Here's an example.
user=> (defn foo
"does many great things"
{:arglists '([a b c] [d e f g])}
[arg] arg)
#'user/foo
user=> (doc foo)
-------------------------
user/foo
([a b c] [d e f g])
does many great things
nil
(In that case, arglists is a total lie. Don't do that!)
alter-meta! changes the metadata on a var. The metadata on the function is not relevant, only the var.
(alter-meta! #'tripler assoc :arglists '([b]))
defn does not leave room to mangle the metadata which is OK because it's just a macro that wraps def. You can use def directly instead of defn:
core> (def ^{:arglists '([b])} tripler (fn [a] (* 3 a)))
#'core/tripler
core> (meta #'tripler)
{:arglists ([b]), :ns #<Namespace autotestbed.core>, :name tripler, :line 1, :file "NO_SOURCE_FILE"}
or you define the var tripler with defn:
core> (defn tripler [a] (* 3 a))
#'autotestbed.core/tripler
then redefine the var with the same contents and different metadata:
core> (def ^{:arglists '([b])} tripler tripler)
#'autotestbed.core/tripler
autotestbed.core> (meta #'tripler)
{:arglists ([b]), :ns #<Namespace autotestbed.core>, :name tripler, :line 1, :file "NO_SOURCE_FILE"}
Expanding on amalloy's answer (please give him credit):
user=> (defn foo "prints bar" [] (println "bar"))
#'user/foo
user=> (doc foo)
-------------------------
user/foo
([])
prints bar
nil
user=> (meta #'foo)
{:arglists ([]), :ns #<Namespace user>, :name foo, :doc "prints bar", :line 1, :file "NO_SOURCE_PATH"}
user=> (alter-meta! #'foo assoc :arglists '([blah]))
{:arglists ([blah]), :ns #<Namespace user>, :name foo, :doc "prints bar", :line 1, :file "NO_SOURCE_PATH"}
user=> (doc foo)
-------------------------
user/foo
([blah])
prints bar
nil
user=> (meta #'foo)
{:arglists ([blah]), :ns #<Namespace user>, :name foo, :doc "prints bar", :line 1, :file "NO_SOURCE_PATH"}
user=> (foo)
bar
nil
Sneaky!

How to set default values for fields in records in Clojure?

I am creating records in Clojure and would like to set some fields up with a default value. How can I do this?
Use a constructor function.
(defrecord Foo [a b c])
(defn make-foo
[& {:keys [a b c] :or {a 5 c 7}}]
(Foo. a b c))
(make-foo :b 6)
(make-foo :b 6 :a 8)
Of course there are various variations. You could for example require certain fields to be non-optional and without a default.
(defn make-foo
[b & {:keys [a c] :or {a 5 c 7}}]
(Foo. a b c))
(make-foo 6)
(make-foo 6 :a 8)
YMMV.
You can pass initial values to a record pretty easily when you construct it though an extension map:
(defrecord Foo [])
(def foo (Foo. nil {:bar 1 :baz 2}))
In light of this, I usually create a constructor function that merges in some default values (which you can override as you want):
(defn make-foo [values-map]
(let [default-values {:bar 1 :baz 2}]
(Foo. nil (merge default-values values-map))))
(make-foo {:fiz 3 :bar 8})
=> #:user.Foo{:fiz 3, :bar 8, :baz 2}
After having the same question, I ended up wrapping the defrecord and the factory function up into a single definition using a macro.
The macro:
(defmacro make-model
[name args & body]
(let [defaults (if (map? (first body)) (first body) {})
constructor-name (str/lower-case (str "make-" name))]
`(do (defrecord ~name ~args ~#(if (map? (first body)) (rest body) body))
(defn ~(symbol constructor-name)
([] (~(symbol constructor-name) {}))
([values#] (~(symbol (str "map->" name)) (merge ~defaults values#)))))))
Usage
(make-model User [firstName lastName] {:lastName "Smith"})
=> #'user/make-user
(make-user {:firstName "John"})
=> #user.User{:firstName "John", :lastName "Smith"}