This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-09-21
Channels
- # announcements (3)
- # architecture (5)
- # babashka (19)
- # beginners (145)
- # calva (1)
- # chlorine-clover (18)
- # cider (10)
- # clj-kondo (5)
- # cljsrn (3)
- # clojure (56)
- # clojure-berlin (19)
- # clojure-czech (2)
- # clojure-europe (32)
- # clojure-finland (2)
- # clojure-nl (3)
- # clojure-portugal (2)
- # clojure-spec (8)
- # clojure-uk (48)
- # clojurescript (48)
- # conjure (50)
- # cryogen (1)
- # cursive (28)
- # datomic (54)
- # depstar (12)
- # emacs (1)
- # events (8)
- # figwheel-main (6)
- # fulcro (4)
- # helix (4)
- # java (2)
- # jobs (2)
- # leiningen (1)
- # off-topic (29)
- # parinfer (4)
- # pathom (6)
- # portkey (3)
- # reagent (1)
- # remote-jobs (3)
- # reveal (16)
- # shadow-cljs (42)
- # sql (20)
- # tools-deps (11)
- # vim (4)
- # vrac (2)
@suren clojure.test
is built-in. Most people use that. Works well with all the editors and command-line tooling out there.
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?
(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
(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
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.In the top example, the first argument shows how it is a map of index to keyword/value
also when you supply replacement pairs as in the second example, it seems to replace by value, not 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
(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))))))
(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)
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))))))))
Or you put the key-value pairs into an un-ordered collection and call distinct
.
(into {} (map vec (distinct (map set m))))
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.
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
We also know that we couldn't rely on the "first-seen" or "last-seen" semantics because the input map has no order.
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.
That was it. Had to make a little java shim that setContextClassLoader
before loading Clojure. Is there any way to do this with just clojure?