Fork me on GitHub
#clojure
<
2020-09-21
>
suren01:09:21

Whats the preferred package for TDD in clojure?

seancorfield02:09:18

@suren clojure.test is built-in. Most people use that. Works well with all the editors and command-line tooling out there.

suren11:09:41

Thanks buddy

practicalli-johnny11:09:51

http://practicalli.github.io/clojure/testing/unit-testing/ is an intro to unit testing in Clojure with some example projects.

restenb11:09:18

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?

delaguardo11:09:54

(apply assoc [1 2 3] (interleave '(0 2) '(:x :y)))

delaguardo11:09:22

where '(0 2) is for list of indices and '(:x :y) for list of values

delaguardo11:09:49

if you need to catch IndexOutOfBound exception you can use loop or reduce instead of apply for more precise control

✔️ 3
restenb11:09:37

I can feel there must be a simple solution to this but my brain is stuck ... 😛

jsn11:09:26

(reduce (partial apply assoc) [1 2 3 4 5 6] [[1 :a] [3 :b]]) would work, but that's probably not what you mean

nivekuil11:09:52

https://clojuredocs.org/clojure.core/replace but the list of values there is also the list of indexes

restenb11:09:50

replacelooks promising but I can't make heads or tails of those examples

restenb11:09:15

(replace [10 9 8 7 6] [0 2 4])->`[10 8 6]` so it just kept index 0, 2, and 4?

Jimmy Miller16:09:47

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.

nivekuil11:09:58

In the top example, the first argument shows how it is a map of index to keyword/value

restenb11:09:24

i don't know what's more obtuse about this, the docstring or the examples

restenb11:09:08

also when you supply replacement pairs as in the second example, it seems to replace by value, not by index

restenb11:09:19

so still not clear to me how to replace by index

Alex Miller (Clojure team)13:09:48

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

emccue17:09:11

(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))))))

emccue17:09:16

here ya go

emccue17:09:38

the when there is to normalize one of the more odd behaviors of assoc

emccue17:09:28

(assoc ['a] 0 'b)
=> [b]
(assoc ['a] 1 'b)
=> [a b]
(assoc ['a] 2 'b)
Execution error (IndexOutOfBoundsException) 

emccue17:09:57

(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)

emccue17:09:11

some more examples of usage there to help you doodle out unit tests

nick18:09:55

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]}

nick18:09:04

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]})

Chris O’Donnell18:09:42

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?)

nick19:09:15

yep, in my case it's not important. Thanks for checking!

👍 3
emccue19:09:04

(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))))))))

🙏 3
Eddie19:09:30

Or you put the key-value pairs into an un-ordered collection and call distinct.

(into {} (map vec (distinct (map set m))))

🙏 3
Eddie19:09:14

But you lose control over which key-value pair you preserve.

emccue19:09:30

imo there are worse things than being explicit

emccue19:09:45

so my loop example is the loser in terms of lines of code

emccue19:09:55

but super clear what it does

jsn19:09:23

@emccue well, it also does the wrong thing, as far as I can see

jsn19:09:07

so no, I disagree; I think the @erp12's variant is much more clear, instantly readable

Eddie19:09:35

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.

jsn19:09:09

yeah, a set of sets could be a better fit

jsn19:09:39

(which you almost do construct anyway before turning it into a map)

👍 3
Eddie19:09:46

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.

Eddie19:09:33

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.

jsn19:09:33

If 'which pair' is some kind of orderning, we could just enforce it in the one-liner, instead of map set , though

jsn19:09:39

no need for loop for that

Eddie19:09:27

Good point @jason358. (map sort m) could work in many cases.

Eddie19:09:03

We also know that we couldn't rely on the "first-seen" or "last-seen" semantics because the input map has no order.

jsn19:09:18

well, that would change the effect on non-duplicate pairs, though

👍 3
jsn19:09:39

which brings us back to the question of if map structure is really a good fit 🙂

sparkofreason23:09:40

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" 

Alex Miller (Clojure team)23:09:10

that's just telling you it can't find clojure on the classpath

Alex Miller (Clojure team)23:09:43

or whatever classloader is doing this load

sparkofreason23:09:32

Clojure should be on the classpath. It is in the uberjar.

sparkofreason23:09:41

I suspect it needs setContextClassLoader someplace.

sparkofreason00:09:43

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?