Converting a list back to a map of maps - clojure

I have a function that returns a struct that has two fields, :key :event. The :event field is a map (decomposed Java object retrieved from a cache). In the REPL, I see the return value as a map.
I then apply, (def events (map #(make-event %) (keys events-cache))), applying the make-event function for each key from the cache, and want a map back containing each event map keyed by key.
What I get back is that, but inside a list. So calling any map functions, to search etc., throws an error, clojure.lang.LazySeq cannot be cast to clojure.lang.IFn.
I'm sure I'm thinking about this all wrong, but is there a way to pull the map of maps from the list?

Maybe this is what you want?
(into {} (for [k (keys events-cache)]
[k (make-event k)]))

Your terms are vague, and the error message you post suggests the issue is of a very different sort than the question you're asking. You're likely to get more help if you post some code, and especially a real stacktrace.
But in general, this error message says "You have a lazy seq object that you are trying to call as a function", like:
(let [m1 (map some-function whatever)
m2 (...more stuff...)]
(m1 m2))
If you want to return a two-element list of m1 and m2, rather than calling m1 as a function with m2 as an argument, you want to use the list function:
(list m1 m2)

Assuming you don't care about whatever the values of events-cache were, and that you want to end up with a map of the events-cache keys to the things you generate with make-event, you could do:
(def events
(let [event-keys (keys events-cache)]
(zipmap event-keys (map make-event event-keys))))
I'm not sure why you'd have a cache that includes values, but then not use those values, but that's another question :)

And just for fun:
(into {} (map (juxt identity make-event) event-keys))

Related

How to iterate over a clojure eduction - without creating a seq?

For the sake of this question, let's assume I created the following eduction.
(def xform (map inc))
(def input [1 2 3])
(def educt (eduction xform input))
Now I want to pass educt to some function that can then do some kind of reduction. The reason I want to pass educt, rather than xform and input is that I don't want to expose xform and input to that function. If I did, that function could simply do a (transduce xform f init input). But as I don't, that function is left with an eduction that cannot be used with transduce.
I know I can e.g. use doseq on eductions, but I believe this will create a seq - with all its overhead in terms of object instantiation and usage for caching.
So how can I efficiently and idiomatically iterate over an eduction?
As eductions implement java.lang.Iterable, this question probably generalizes to:
How to iterate over a java.lang.Iterable without creating a seq?
reduce can be used to do that.
It works on instances of IReduceInit, which eduction implements.

Create a map entry in Clojure

What is the built-in Clojure way (if any), to create a single map entry?
In other words, I would like something like (map-entry key value). In other words, the result should be more or less equivalent to (first {key value}).
Remarks:
Of course, I already tried googling, and only found map-entry? However, this document has no linked resources.
I know that (first {1 2}) returns [1 2], which seems a vector. However:
(class (first {1 2}))
; --> clojure.lang.MapEntry
(class [1 2])
; --> clojure.lang.PersistentVector
I checked in the source code, and I'm aware that both MapEntry and PersistentVector extend APersistentVector (so MapEntry is more-or-less also a vector). However, the question is still, whether I can create a MapEntry instance from Clojure code.
Last, but not least: "no, there is no built in way to do that in Clojure" is also a valid answer (which I strongly suspect is the case, just want to make sure that I did not accidentally miss something).
"no, there is no built in way to do that in Clojure" is also a valid answer
Yeah, unfortunately that's the answer. I'd say the best you can do is define a map-entry function yourself:
(defn map-entry [k v]
(clojure.lang.MapEntry/create k v))
Just specify a class name as follows
(clojure.lang.MapEntry. "key" "val")
or import the class to instantiate by a short name
(import (clojure.lang MapEntry))
(MapEntry. "key" "val")
As Rich Hickey says here: "I make no promises about the continued existence of MapEntry. Please don't use it." You should not attempt to directly instantiate an implementation class such clojure.lang.MapEntry. It's better to just use:
(defn map-entry [k v] (first {k v}))

Trying to get a lazy-contains? function in Clojure to return results consistant with contains? when dealing with a map

Here's a use of the standard 'contains?' function in Clojure-
(contains? {:state "active", :course_n "law", :course_i "C0"} :state)
and it returns the expected
true
I used the following
Clojure: Idiomatic way to call contains? on a lazy sequence
as a guide for building a lazy-contains? as this is what I need for my present use-case.
The problem I'm facing is that for a map these alternatives are not returning the same answer, giving either a false or a nil response. I've tried looking at the source for contains? and it's slow going trying to understand what's happening so I can correct the lazy-contains? appropriately (for the record Clojure is essentially my first programming language, and my exposure to Java is very limited).
Any thoughts or ideas on how I might approach this? I tried every variant on the linked question I could.
Thanks in advance.
Edited to remove the error pointed out by #amalloy.
I think your problem is with the way that maps present themselves as sequences.
Given
(def data {:state "active", :course_n "law", :course_i "C0"})
then
(seq data)
;([:state "active"] [:course_i "C0"] [:course_n "law"])
... a sequence of key-value pairs.
So if we define (following #chouser)
(defn lazy-contains? [coll x]
(some #(= x %) coll))
... then
(lazy-contains? data :state)
;nil
... a false result, whereas ...
(lazy-contains? data [:state "active"])
;true
This is what #Ankur was getting at, showing you a function treating a map as a sequence consistent with contains? on the map itself.
The standard contains? works with keyed/indexed collections - maps
or sets or vectors - where it tests for the presence of a key.
Our lazy-contains? works with anything sequable, including all the
standard collections, testing for the presence of a value.
Given the way that keyed/indexed collections present as sequences, these are bound to be inconsistent.
You can try the below implementation (for maps only):
(defn lazy-contains? [col key]
(some (fn [[k v]] (= k key)) col))
Remember, contains? is to check the existence of a key in a collection, in maps the key is obvious, in other supported collections (like vector) the key is the index.
A "lazy" implementation of contains? is undesirable where checking for presence
of a key in a hash-map or of a value in a set
(contains? #{:foo} :foo}) => true
(contains? {:foo :bar} :foo) => true
of an index of a vector array or string.
(contains? [:foo] 0) => true
(contains? (int-array 7) 6) => true
(contains? "foo" 2) => true
Quoting from the contains? docstring:
'contains?' operates constant or logarithmic time; it will not
perform a linear search for a value.
some is a tool for linear searching. When searching for an element in a set or vector, it can take the input sequence length times as long as contains? or longer in the worst case and will take more time than contains? in almost every case.
contains? can't be implemented "lazy" as it does not produce a sequence. However, some stops consuming a lazy sequence as soon as it has determined a return value.
(some zero? (range))
;; true
Notice that maps and sets are never sequential or lazy.

In clojure, why does assoc require arguments in addition to a map, but dissoc not?

In clojure,
(assoc {})
throws an arity exception, but
(dissoc {})
does not. Why? I would have expected either both of them to throw an exception, or both to make no changes when no keys or values are provided.
EDIT: I see a rationale for allowing these forms; it means we can apply assoc or dissoc to a possibly empty list of arguments. I just don't see why one would be allowed and the other not, and I'm curious as to whether there's a good reason for this that I'm missing.
I personally think the lack of 1-arity assoc is an oversight: whenever a trailing list of parameters is expected (& stuff), the function should normally be capable of working with zero parameters in order to make it possible to apply it to an empty list.
Clojure has plenty of other functions that work correctly with zero arguments, e.g. + and merge.
On the other hand, Clojure has other functions that don't accept zero trailing parameters, e.g. conj.
So the Clojure API is a bit inconsistent in this regard.....
This is not an authoritative answer, but is based on my testing and looking at ClojureDocs:
dissoc 's arity includes your being able to pass in one argument, a map. No key/value is removed from the map, in that case.
(def test-map {:account-no 12345678 :lname "Jones" :fnam "Fred"})
(dissoc test-map)
{:account-no 12345678, :lname "Jones", :fnam "Fred"}
assoc has no similar arity. That is calling assoc requires a map, key, and value.
Now why this was designed this way is a different matter, and if you do not receive an answer with that information -- I hope you do -- then I suggest offering a bounty or go on Clojure's Google Groups and ask that question.
Here is the source.
(defn dissoc
"dissoc[iate]. Returns a new map of the same (hashed/sorted) type,
that does not contain a mapping for key(s)."
{:added "1.0"
:static true}
([map] map)
([map key]
(. clojure.lang.RT (dissoc map key)))
([map key & ks]
(let [ret (dissoc map key)]
(if ks
(recur ret (first ks) (next ks))
ret))))

Clojure: working with a java.util.HashMap in an idiomatic Clojure fashion

I have a java.util.HashMap object m (a return value from a call to Java code) and I'd like to get a new map with an additional key-value pair.
If m were a Clojure map, I could use:
(assoc m "key" "value")
But trying that on a HashMap gives:
java.lang.ClassCastException: java.util.HashMap cannot be cast to clojure.lang.Associative
No luck with seq either:
(assoc (seq m) "key" "value")
java.lang.ClassCastException: clojure.lang.IteratorSeq cannot be cast to clojure.lang.Associative
The only way I managed to do it was to use HashMap's own put, but that returns void so I have to explicitly return m:
(do (. m put "key" "value") m)
This is not idiomatic Clojure code, plus I'm modifying m instead of creating a new map.
How to work with a HashMap in a more Clojure-ish way?
Clojure makes the java Collections seq-able, so you can directly use the Clojure sequence functions on the java.util.HashMap.
But assoc expects a clojure.lang.Associative so you'll have to first convert the java.util.HashMap to that:
(assoc (zipmap (.keySet m) (.values m)) "key" "value")
Edit: simpler solution:
(assoc (into {} m) "key" "value")
If you're interfacing with Java code, you might have to bite the bullet and do it the Java way, using .put. This is not necessarily a mortal sin; Clojure gives you things like do and . specifically so you can work with Java code easily.
assoc only works on Clojure data structures because a lot of work has gone into making it very cheap to create new (immutable) copies of them with slight alterations. Java HashMaps are not intended to work in the same way. You'd have to keep cloning them every time you make an alteration, which may be expensive.
If you really want to get out of Java mutation-land (e.g. maybe you're keeping these HashMaps around for a long time and don't want Java calls all over the place, or you need to serialize them via print and read, or you want to work with them in a thread-safe way using the Clojure STM) you can convert between Java HashMaps and Clojure hash-maps easily enough, because Clojure data structures implement the right Java interfaces so they can talk to each other.
user> (java.util.HashMap. {:foo :bar})
#<HashMap {:foo=:bar}>
user> (into {} (java.util.HashMap. {:foo :bar}))
{:foo :bar}
If you want a do-like thing that returns the object you're working on once you're done working on it, you can use doto. In fact, a Java HashMap is used as the example in the official documentation for this function, which is another indication that it's not the end of the world if you use Java objects (judiciously).
clojure.core/doto
([x & forms])
Macro
Evaluates x then calls all of the methods and functions with the
value of x supplied at the front of the given arguments. The forms
are evaluated in order. Returns x.
(doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))
Some possible strategies:
Limit your mutation and side-effects to a single function if you can. If your function always returns the same value given the same inputs, it can do whatever it wants internally. Sometimes mutating an array or map is the most efficient or easiest way to implement an algorithm. You will still enjoy the benefits of functional programming as long as you don't "leak" side-effects to the rest of the world.
If your objects are going to be around for a while or they need to play nicely with other Clojure code, try to get them into Clojure data structures as soon as you can, and cast them back into Java HashMaps at the last second (when feeding them back to Java).
It's totally OK to use the java hash map in the traditional way.
(do (. m put "key" "value") m)
This is not idiomatic Clojure code, plus I'm modifying m instead of creating a new map.
You are modifying a data structure that really is intended to be modified. Java's hash map lacks the structural sharing that allows Clojures map's to be efficiently copied. The generally idiomatic way of doing this is to use java-interop functions to work with the java structures in the typical java way, or to cleanly convert them into Clojure structures and work with them in the functional Clojure way. Unless of course it makes life easier and results in better code; then all bets are off.
This is some code I wrote using hashmaps when I was trying to compare memory characteristics of the clojure version vs java's (but used from clojure)
(import '(java.util Hashtable))
(defn frequencies2 [coll]
(let [mydict (new Hashtable)]
(reduce (fn [counts x]
(let [y (.toLowerCase x)]
(if (.get mydict y)
(.put mydict y (+ (.get mydict y) 1))
(.put mydict y 1)))) coll) mydict))
This is to take some collection and return how many times each different thing (say a word in a string) is reused.