@joao.galrito Welcome! If you're just getting started with Clojure, ask lots of questions in the #beginners channel -- that's where folks have opted in to helping new Clojurians and you'll find lots of willing minds to pick there.
If you have questions about specific libraries, there are likely library-specific channels where you can ask those questions (e.g., #sql for database stuff, #honeysql #hugsql for specific ilbraries, #ring #component #integrant #hiccup etc -- some of them can be pretty quiet but that means there's also more chance that an expert can help there).
@seancorfield thank you. I'd say I'm past the beginner phase. In this case I'm looking for an opinion and some tips/suggestions on a project I'm working on. Mainly if my approach and use of the language is sound
hit me
Whats the preferred package for TDD in clojure?
@suren clojure.test
is built-in. Most people use that. Works well with all the editors and command-line tooling out there.
Thanks buddy
http://practicalli.github.io/clojure/testing/unit-testing/ is an intro to unit testing in Clojure with some example projects.
so I know you can replace vector elements simply with assoc
: (assoc [1 2 3] 1 4)
->`[1 4 3]` f.ex. But what's a simple way to, given a list of indexes and a list of values, replace all indexes in that vector with the corresponding value?
I can feel there must be a simple solution to this but my brain is stuck ... 😛
(reduce (partial apply assoc) [1 2 3 4 5 6] [[1 :a] [3 :b]])
would work, but that's probably not what you mean
https://clojuredocs.org/clojure.core/replace but the list of values there is also the list of indexes
(apply assoc [1 2 3] (interleave '(0 2) '(:x :y)))
where '(0 2)
is for list of indices
and '(:x :y)
for list of values
if you need to catch IndexOutOfBound exception you can use loop or reduce instead of apply for more precise control
replace
looks promising but I can't make heads or tails of those examples
(replace [10 9 8 7 6] [0 2 4])
->`[10 8 6]` so it just kept index 0, 2, and 4?
In the top example, the first argument shows how it is a map of index to keyword/value
i don't know what's more obtuse about this, the docstring or the examples
also when you supply replacement pairs as in the second example, it seems to replace by value, not by index
so still not clear to me how to replace by index
replace
works on the values, not the indexes so doesn't do what you want. I don't think there is a function to do this for you, but you can use reduce or a loop to repeatedly assoc
I agree that these examples are confusing. Here is are two equivalent statements for replace that maybe help clarify.
(replace {0 10
1 9
2 8
3 7
4 6}
[0 1 2 3 4])
(replace [10 9 8 7 6]
[0 1 2 3 4])
You can think about a vector as a map from indexes to values. That is what the example is taking advantage of. Replace is looking up its value in the map that is provided to find its replacement. So in this case, you can think of the code like this
(let [replacements [10 9 8 7 6]
my-coll [0 1 2 3 4]]
(mapv
(fn [x] (or (get replacements x) x))
my-coll))
I know that doesn't help solve your problem. Just wanted to explain what was going on.(defn replace-at-indexes
"(replace-at-indexes ['a 'b 'c] [[0 'f] [1 'd]])
=> [f d c]"
[vector index-replacement-pairs]
(loop [pairs index-replacement-pairs
vector vector]
(if (empty? pairs)
vector
(let [[idx val] (first pairs)]
(when (>= idx (count vector))
(throw (IndexOutOfBoundsException.)))
(recur (rest pairs)
(assoc vector idx val))))))
here ya go
the when
there is to normalize one of the more odd behaviors of assoc
(assoc ['a] 0 'b)
=> [b]
(assoc ['a] 1 'b)
=> [a b]
(assoc ['a] 2 'b)
Execution error (IndexOutOfBoundsException)
(replace-at-indexes [] [])
=> []
(replace-at-indexes ['a] [[0 'b]])
=> [b]
(replace-at-indexes ['a] [[1 'b]])
Execution error (IndexOutOfBoundsException)
(replace-at-indexes [] [])
=> []
(replace-at-indexes ['a] [[0 'b]])
=> [b]
(replace-at-indexes ['a] [[1 'b]])
Execution error (IndexOutOfBoundsException)
some more examples of usage there to help you doodle out unit tests
Any suggestions on how I can remove "duplicate key/values" from this map are greatly appreciated
By "duplicate key/values" I mean pairs like
[:foo 1] [:bar 2]
& [:bar 2] [:foo 1]
(custom-distinct
{[:foo 1] [:bar 2]
[:bar 2] [:foo 1]
[:foo 4539] [:google_play 1]
})
;; expected:
;; {[:foo 1] [:bar 2]
;; [:foo 4539] [:google_play 1]}
This is embarrassing the simplest reduce-kv seemed to be solve the issue.
(reduce-kv (fn [acc k v]
(if (or (get acc k) (get acc v))
acc
(assoc acc k v)))
{}
{[:foo 1] [:bar 2]
[:bar 2] [:foo 1]
[:foo 4539] [:google_play 1]})
Why is the [:bar 2]
key removed and not [:foo 1]
? Your algorithm doesn't seem deterministic. (Maybe that doesn't matter in this case?)
(defn distinct-keys-and-values [m]
(loop [keys-and-vals (seq m)
new-map {}
seen #{}]
(if (empty? keys-and-vals)
new-map
(let [[k v] (first keys-and-vals)]
(if (or (contains? seen k)
(contains? seen v))
(recur (rest keys-and-vals)
new-map
seen)
(recur (rest keys-and-vals)
(assoc new-map k v)
(-> seen
(conj k)
(conj v))))))))
yep, in my case it's not important. Thanks for checking!
Or you put the key-value pairs into an un-ordered collection and call distinct
.
(into {} (map vec (distinct (map set m))))
But you lose control over which key-value pair you preserve.
imo there are worse things than being explicit
so my loop example is the loser in terms of lines of code
but super clear what it does
Agreed.
@emccue well, it also does the wrong thing, as far as I can see
so no, I disagree; I think the @erp12's variant is much more clear, instantly readable
One could also argue that the original data doesn't belong in a map to begin with. If it doesn't matter which value is the key and which is the value, then I would think of the data as a set of unordered pairs.
yeah, a set of sets could be a better fit
(which you almost do construct anyway before turning it into a map)
As the for clarity of the one-liner, there is definitely ambiguity as to what will be included in the final map, which isn't great.... but on the other hand if we are considering [a b]
to be a duplicate of [b a]
then we are stating that we don't care.
There could be a use case for being more explicit about which pair you preserve, and that would require some kind of loop or reduce.
If 'which pair' is some kind of orderning, we could just enforce it in the one-liner, instead of map set
, though
no need for loop for that
Good point @jason358. (map sort m)
could work in many cases.
We also know that we couldn't rely on the "first-seen" or "last-seen" semantics because the input map has no order.
well, that would change the effect on non-duplicate pairs, though
which brings us back to the question of if map structure is really a good fit 🙂
Hitting what I assume is a class loader issue. Trying to make a Google cloud function with JVM clojure, getting the error below. The class is created using gen-class
, wondering if that isn't going to for the situation where the environment is using a different class loader.
"Exception in thread "main" java.lang.ExceptionInInitializerError
at clojure.lang.Namespace.<init>(Namespace.java:34)
at clojure.lang.Namespace.findOrCreate(Namespace.java:176)
at clojure.lang.Var.internPrivate(Var.java:156)
at ck.proxysql_notifier.<clinit>(Unknown Source)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at com.google.cloud.functions.invoker.NewHttpFunctionExecutor.forClass(NewHttpFunctionExecutor.java:51)
at com.google.cloud.functions.invoker.runner.Invoker.startServer(Invoker.java:243)
at com.google.cloud.functions.invoker.runner.Invoker.main(Invoker.java:129)
Caused by: java.io.FileNotFoundException: Could not locate clojure/core__init.class, clojure/core.clj or clojure/core.cljc on classpath.
at clojure.lang.RT.load(RT.java:462)
at clojure.lang.RT.load(RT.java:424)
at clojure.lang.RT.<clinit>(RT.java:338)
... 11 more"
that's just telling you it can't find clojure on the classpath
or whatever classloader is doing this load
Clojure should be on the classpath. It is in the uberjar.
I suspect it needs setContextClassLoader
someplace.