Python setdefault function in Clojure - clojure

I am trying to do something similar in Clojure:
# Increase the count of a feature/category pair
def incf(self,f,cat):
self.fc.setdefault(f,{})
self.fc[f].setdefault(cat,0)
self.fc[f][cat]+=1
Does anybody have idea?

I think a close parallel is fnil which takes a function and some default argument value(s), then returns a function that will use the default value if called with nil argument(s):
(defn incf [fc f cat]
(update-in fc [f cat] (fnil inc 0)))
(incf {} :feature-foo :category-bar)
=> {:feature-foo {:category-bar 1}}
Here we use update-in to update a nested value in the input map, and use fnil to set the default value zero to be incremented if it doesn't exist.

First, you cannot mutate a hash map in Clojure, so there is no exact counterpart for setdefault.
However, if you want to update a nested map increasing the given value (or set to 1, if it is null), you can leverage the fact that clojure.core/get can accept an optional third argument which is the default value (and also, an assoc on nil creates a map):
(defn incf [m cat f]
(let [val (get-in m [cat f] 0)]
(assoc-in m [cat f] (inc val))))
(incf {:my-cat {:a 1}} :my-cat :a) ; returns {:my-cat {:a 2}}
(incf {:my-cat {}} :my-cat :a) ; returns {:my-cat {:a 1}}
(incf {} :my-cat :a) ; returns {:my-cat {:a 1}}

Related

Using Java hashmaps in Clojure

I am new to Clojure. I am trying to use java hashmap in clojure. I am passing a java hashmap to Clojure. The map is- {0=Goa, 1=Delhi, 2=Mumbai}. When I am trying to use the clojure functions on this map I am not getting the expected output. In contrast to this when I am iterating over this map it is giving the expected output.
Example
(println(get map 0)) is giving nil
(doseq [[key value] map
(println value)) is giving the expected output.
Output-Goa
Delhi
Mumbai
Can somebody please expain me why is this happening?
You really should google a bit to find pre-existing answers like this one: Clojure: working with a java.util.HashMap in an idiomatic Clojure fashion
You can then see a simple answer:
(def data {:a 1 :b 2 :c 3})
(def java-map (java.util.HashMap. data))
(def clj-map (into {} java-map))
which gives us:
java-map => <#java.util.HashMap {:b 2, :c 3, :a 1}>
clj-map => <#clojure.lang.PersistentArrayMap {:b 2, :c 3, :a 1}>
and looping:
(doseq [[k v] clj-map]
(println (format "key=%s val=%s" k v)) )
with result:
key=:b val=2
key=:c val=3
key=:a val=1
I think your problem is that your map is named "map" which is also a Clojure function. Try it like this:
(def my-map {0 "Goa" 1 "Delhi" 2 "Mumbai"})
Which then will work like this:
(println (get my-map 0))
Note that it still returns nil, since there is nothing else after the (println) form but it does print the value of the 0 in the map, which is "Goa".
(def input-map {0 "Goa" 1 "Delhi" 2 "Mumbai"})
(map (fn[[k v]] (print "key " k " value " k)) input-map)
[[k v]] for function let you access key and value for each entry
(map print input-map)
here map entry will be passed as parameter to print

Clojure - Create an array of keys if value is true

I am totally new to clojure.
I have a JSON like: { "1": true, "2": false, "3": true, "4": false }
I want to create an array of keys for which the value is true in clojure. In this example the array should be ["1", "3"].
Please help me. Any help would be appreciated.
there are also couple of short and simple snippets for that:
user> (filter m (keys m))
;;=> ("1" "3")
user> (keep (fn [[k v]] (when v k)) m)
;;=> ("1" "3")
user> (for [[k v] m :when v] k)
;;=> ("1" "3")
If you're fine with using a vector instead of an array (since you're usually using vectors in Clojure anyway), you can do something like.
(defn keys-for-truthy-vals [m]
(->> m (filter val) (mapv key)))
Note The mapv is only so the map call returns a vector. If you want a seq, just use map.
The same as already provided, just staying in maps.
(keys (filter val m))
If your map is a Something like (->> (filter (fn [[k v]] v) a) (map (fn [[k v]] k))) will work. You can't do it with just a map because you need to drop certain values, so there will need to be some reducing or filtering.
There is built-in function in the Tupelo library for this:
(submap-by-vals map-arg keep-vals & opts)
Returns a new map containing entries with the specified vals. Throws for missing vals,
unless `:missing-ok` is specified. Usage:
(submap-by-vals {:a 1 :b 2 :A 1} #{1 } ) => {:a 1 :A 1}
(submap-by-vals {:a 1 :b 2 :A 1} #{1 9} :missing-ok ) => {:a 1 :A 1}
You could then just use the keys function on the resulting map.
Maybe this?
(->> foo (filter second) keys)
where foo is a map.

get-in for lists

Apparently get-in doesn't work for '() lists since they're not an associative data structure. This makes sense for the API and from the perspective of performance of large lists. From my perspective as a user it'd be great to still use this function to explore some small test data in the repl. For example I want to be able to:
(-> '({:a ("zero" 0)} {:a ("one" 1)} {:a ("two" 2)})
(get-in [1 :a 0]))
=> "one"
Is there some other function that works this way? Is there some other way to achieve this behavior that doesn't involve converting all my lists to (say) vectors?
This does what you ask:
(defn get-nth-in [init ks]
(reduce
(fn [a k]
(if (associative? a)
(get a k)
(nth a k)))
init
ks))
For example,
(-> '({:a "zero"} {:a "one"} {:a "two"})
(get-nth-in [1 :a]))
;"one"
and
(-> '({:a ("zero" 0)} {:a ("one" 1)} {:a ("two" 2)})
(get-nth-in [1 :a 0]))
;"one"
The extra 's you have get expanded into (quote ...):
(-> '({:a '("zero" 0)} {:a '("one" 1)} {:a '("two" 2)})
(get-nth-in [1 :a 0]))
;quote
Not what you intended, I think.
A post just yesterday had a problem regarding lazy lists and lazy maps (from clojure/data.xml). One answer was to just replace the lazy bits with plain vectors & maps using this function:
(defn unlazy
[coll]
(let [unlazy-item (fn [item]
(cond
(sequential? item) (vec item)
(map? item) (into {} item)
:else item))
result (postwalk unlazy-item coll)
]
result ))
Since the resulting data structure uses only vectors & maps, it works for your example with get-in:
(let [l2 '({:a ("zero" 0)} {:a ("one" 1)} {:a ("two" 2)})
e2 (unlazy l2) ]
(is= l2 e2)
(is= "one" (get-in e2 [1 :a 0] l2))
)
You can find the unlazy function in the Tupelo library.
The first param for get-in should be a map.
You have to figure out the feature of your sequence, use last, first, filter or some e.g. to get the element first
for example you could use (:a (last data))

Clojure : what am I missing from comp?

I'm currenthly trying to work with compand it looks like I am missing something.
If I understand well, comp works in the same order as the mathematical composition, so (comp g f) is like g(f(x)).
Imagine I have a map like that
(def m {:a 1 :b nil :c 3})
I would like to use remove with a short nil-key? function to remove the entries which have nil values, so :
(into {} (remove nil-key? m)) = {:a 1 :c 3}
I tried to define nil-key? like that :
(defn nil-key? []
(comp nil? second))
It returns an empty map (if I use filter, no map entry is removed)
Maybe I do not understand how the remove function works because I thught there was an hidden map.
Like : 1) first map second on the hashmap
2) tells if the value is nil
3) give the matching
I could do
(into {} (filter second m))
But is also removes false, which I want not.
Of course I can do it easily with a different approach but I would like to understand the comp function.
Thanks !
EDIT
The answer
(def nil-key?
(comp nil? second))
The final function
(defn remove-nil-keys [map]
(->> (remove nil-key? map)
(into {})))
You have a mistake in your nil-key? definition. Your function returns a function that will produce a composed function:
(defn nil-key? []
(comp nil? second))
If you want to use it in this form you would have to call nil-key? in order to produce your predicate function:
(into {} (remove (nil-key?) m))
;; => {:a 1, :c 3}
Instead you should define a var with the result of composing the functions:
(def nil-key? (comp nil? second))
Then it will work correctly:
(into {} (remove nil-key? m))
;; => {:a 1, :c 3}

defn vs. let with regards to decomposition

I define a function, which takes two parameters - map and a key. The key is referenced from the map parameter decomposition
(defn myfunc [{v k} k]
v)
when I call:
(myfunc {:a 10} :a)
It surprisingly produces expected result: 10
Similar thing in the let:
(let [{v k} {:a 10} k :a] v)
fails, because k is not defined at the moment, when first part is evaluated.
My question is: why decomposition inside function parameters behaves differently compared to decomposition in let expressions?
Macroexpanding the defn form I get the equivalent of this (removed .withMeta stuff and reformatted):
(def myfunc
(fn* myfunc
([p__11393 k]
(let* [map__11394 p__11393
map__11394 (if (seq? map__11394)
(apply hash-map map__11394)
map__11394)
v (get map__11394 k)]
v))))
So here we can see that the {v k} map is in fact first assigned to a local variable p__11393. Then the if statement tests if that variable is in fact a sequence and turns it into a hash-map if so, otherwise leaves it as it is. Importantly, the value assigned to k is looked up in the map after all of this happens, thus this works without error (and also would if :a wasn't in the map, get returns nil in that case).
On the other hand macroexpanding the let form I get
(let*
[map__11431
{:a 10}
map__11431
(if (seq? map__11431) (apply hash-map map__11431) map__11431)
v
(get map__11431 k)
k
:a]
v)
and here we can see that v gets the result of (get map__11431 k), but k isn't defined at this point yet, hence the error.
This isn't a complete answer, but the following does work:
user=> (defn myfunc [{v k} k] v)
#'user/myfunc
user=> (myfunc {:a 10} :a)
10
user=> (let [k :a {v k} {:a 10}] v)
10
user=>