beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
2021-03-16T00:12:44.041200Z

laziness is not a promise that as few as possible elements are realized, it's the possibility that fewer than the total number are realized

2021-03-16T00:13:20.041400Z

if your program logic depends on realizing only N items, either use take before map, or don't use laziness - laziness and side effects mix poorly in general

cschep 2021-03-16T04:12:16.001100Z

I have a vector of maps, each map has a key :id, i’d like for this vector to actually be a map from the :id to a map of the rest of the data in the map

cschep 2021-03-16T04:12:26.001400Z

is there a cool flatten trick here

cschep 2021-03-16T04:12:29.001600Z

extract, pick something

cschep 2021-03-16T04:12:46.001900Z

or do i create an empty map and loop through and assoc in? doesn’t feel very functional

cschep 2021-03-16T04:12:48.002100Z

maybe reduce.

dpsutton 2021-03-16T04:13:30.002500Z

(into {} (map (juxt :id identity)) your-coll)

dpsutton 2021-03-16T04:14:24.003400Z

most codebases end up with some utility function that's u/key-by that does this to some effect. up to you how you resolve if there's the same id in more than one of the maps. first wins, last wins, etc

🎯 3
cschep 2021-03-16T04:16:19.003700Z

oooo

cschep 2021-03-16T04:16:33.004Z

thanks!

cschep 2021-03-16T04:18:56.004700Z

this is really awesome, juxt combines the functions :id, which is a keyword but why do you need identity?

cschep 2021-03-16T04:19:44.005Z

oh that’s the piece that puts the rest of the map in as the value

dpsutton 2021-03-16T04:19:48.005200Z

((juxt :id identity) {:id 1 :other :stuff}) #_ -> [1 {:id 1, :other :stuff}]

dpsutton 2021-03-16T04:20:06.005600Z

yeah. you make tuples of [k v] and then pour those (into {})

cschep 2021-03-16T04:20:34.006200Z

that’s awesome, thanks!

seancorfield 2021-03-16T04:37:10.007Z

This is one of those things where you often hear people say "what use is a function that always just returns its argument?" and here's a great example.

cschep 2021-03-16T04:38:54.007500Z

yeah, I would have asked that question too! but that’s really useful 🙂

0xclj 2021-03-16T08:59:02.008500Z

When should one use Exception. vs ex-info ?

2021-03-16T09:02:31.008800Z

when consumer of your function expects to see specific Throwable class such as Exception

👍 1
0xclj 2021-03-16T09:49:13.009100Z

Thanks!

Aviv Kotek 2021-03-16T11:22:32.009600Z

hi, how can I keep-order when having large elements using group-by? grouping vec (when order matters) is lost on large elements size (result turns to persistent.hashmap rather than persistent.arraymap) i;m interested in the index-ordering to be kept (as in this example)

(->> [{:a 2 :b 2} {:a 1} {:a 1 :b 1}]
     (group-by :a))
     
=> {2 [{:a 2, :b 2}], 1 [{:a 1} {:a 1, :b 1}]}
order is kept, but when there are plenty items the order is lost.

2021-03-16T11:46:10.009700Z

maps in clojure are not ordered in general. you could coerce result of group-by into sorted-map using (into (sorted-map))

(->> [{:a 2 :b 2} {:a 1} {:a 1 :b 1}]
     (group-by :a)
     (into (sorted-map)))

zackteo 2021-03-16T12:26:37.012100Z

Not sure what I'm doing wrong exactly but I can't seem to get this clojars library working with a minimal example https://github.com/kahuin/kahdemlia I added [kahdemlia "0.1.0-SNAPSHOT"] to my project.clj and tried to run

(ns ds-test.core
  (:require [kahdemlia.core :refer [make-node!]]))

zackteo 2021-03-16T12:30:30.012700Z

but i will get

2. Unhandled clojure.lang.Compiler$CompilerException
   Error compiling kahdemlia/core.cljc at (1:1)
   #:clojure.error{:phase :compile-syntax-check,
                   :line 1,
                   :column 1,
                   :source "kahdemlia/core.cljc"}
             Compiler.java: 7648  clojure.lang.Compiler/load
                   RT.java:  381  clojure.lang.RT/loadResourceScript
                   RT.java:  372  clojure.lang.RT/loadResourceScript
                   RT.java:  459  clojure.lang.RT/load
                   RT.java:  424  clojure.lang.RT/load
                  core.clj: 6126  clojure.core/load/fn
                  core.clj: 6125  clojure.core/load
                  core.clj: 6109  clojure.core/load
               RestFn.java:  408  clojure.lang.RestFn/invoke
                  core.clj: 5908  clojure.core/load-one
                  core.clj: 5903  clojure.core/load-one
                  core.clj: 5948  clojure.core/load-lib/fn
                  core.clj: 5947  clojure.core/load-lib
                  core.clj: 5928  clojure.core/load-lib
               RestFn.java:  142  clojure.lang.RestFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 5985  clojure.core/load-libs
                  core.clj: 5969  clojure.core/load-libs
               RestFn.java:  137  clojure.lang.RestFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 6007  clojure.core/require
                  core.clj: 6007  clojure.core/require
               RestFn.java:  408  clojure.lang.RestFn/invoke
                      REPL:    1  ds-test.core/eval19109/loading--auto--
                      REPL:    1  ds-test.core/eval19109
                      REPL:    1  ds-test.core/eval19109
             Compiler.java: 7177  clojure.lang.Compiler/eval
             Compiler.java: 7166  clojure.lang.Compiler/eval
             Compiler.java: 7132  clojure.lang.Compiler/eval
                  core.clj: 3214  clojure.core/eval
                  core.clj: 3210  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  665  clojure.core/apply
                  core.clj: 1973  clojure.core/with-bindings*
                  core.clj: 1973  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java:  137  clojure.lang.RestFn/applyTo
                  core.clj:  665  clojure.core/apply
                  core.clj:  660  clojure.core/apply
                regrow.clj:   20  refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  202  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  201  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  834  java.lang.Thread/run

1. Caused by java.io.FileNotFoundException
   Could not locate clojure/spec__init.class, clojure/spec.clj or
   clojure/spec.cljc on classpath.

Aviv Kotek 2021-03-16T12:30:47.012900Z

I know, but i'd like the sorted map to keep the index ordering

Aviv Kotek 2021-03-16T12:31:56.013100Z

in my example the order is correct (index oredring), but if i'd increase the element size it'll change

2021-03-16T12:34:09.013400Z

There are implementations of maps in Clojure that guarantee that the keys are sorted in the order that they were inserted into the map, e.g. https://github.com/clj-commons/ordered

2021-03-16T12:34:54.013900Z

Clojure's built-in function group-by does not return such a map, but you can copy and paste group-by's code into your own program, and modify it to use such an insertion-ordered map, if you really want to.

2021-03-16T12:36:19.014100Z

Note that many built-in Clojure functions other than group-by will return maps with no ordering guarantees, so if you use maps that preserve insertion order, and you want to keep using them throughout a portion of your data flow in your program, you will have to check whether all of the operations you are performing on those maps preserve the insertion order, or not, and perhaps create your own custom functions that do preserve it.

jkxyz 2021-03-16T12:39:32.014300Z

It’s failing to compile the kahdemlia.core namespace because it requires clojure.spec , which is a namespace that doesn’t exist in later versions of Clojure. You can try explicitly adding [org.clojure/clojure "1.9.0-alpha15"] to your dependencies, and it should compile. But I wouldn’t suggest using this library for anything serious unless you fork it and update it to work with a newer Clojure version

2021-03-16T12:40:41.014500Z

Looking at the definition of group-by, it uses transients on Clojure's built-in maps, which I suspect the insertion-ordered-map implementation does not support. If you want help creating a modified group-by that returns insertion-order-guaranteed maps, let me know.

zackteo 2021-03-16T12:40:56.014700Z

I see I see hmmmm, I'll just clone the repo and explore the library directly instead 🙂

jkxyz 2021-03-16T12:43:02.015Z

That also works for trying it out. The spec library is now under clojure.spec.alpha, it seems like clojure.spec only existed for that alpha release of Clojure. So should be possible to update the library to work with the latest Clojure

alexmiller 2021-03-16T12:55:10.015200Z

generally, when writing Clojure, I almost always use ex-info

✔️ 1
☝️ 1
Aviv Kotek 2021-03-16T13:01:50.015400Z

i'd like yes 🙂

Aviv Kotek 2021-03-16T13:02:34.015600Z

i'd prefer not to add any dependency to the project (ordered clj-commons)

2021-03-16T13:04:02.015800Z

You can implement your own such variant of maps if you want, independent of that project. There is no such insertion-order-guaranteed map built into Clojure

Aviv Kotek 2021-03-16T13:05:07.016Z

I understand, what about staying with persistent.arraymap ?

2021-03-16T13:05:18.016200Z

You can also maintain an unordered map, and separately keep a vector or list that contains the keys in the order you want them to be in, and use the list/vector for traversing keys in the desired order.

2021-03-16T13:05:51.016400Z

array-maps have O(n) time lookup for keys. I doubt you would want that for a 1000-entry map.

Aviv Kotek 2021-03-16T13:06:18.016600Z

my element size is finite < 30x

2021-03-16T13:06:37.016800Z

Only finite things fit into a computer's memory 🙂

2021-03-16T13:07:00.017Z

Do you want an array-map if the lookup time for a key is linear in the number of keys?

Aviv Kotek 2021-03-16T13:07:09.017200Z

yes

2021-03-16T13:07:20.017400Z

So you do not plan to do lookup of keys very often?

Aviv Kotek 2021-03-16T13:07:36.017600Z

I basically do some transformation on the data and send it forward

Aviv Kotek 2021-03-16T13:07:40.017800Z

so there is no lookups anyway

2021-03-16T13:07:51.018Z

I mean, the performance of your code is up to you -- I'm just warning you of the implications.

Aviv Kotek 2021-03-16T13:07:54.018200Z

I have some structured data with a common key (which I group-by on)

Aviv Kotek 2021-03-16T13:08:14.018400Z

and the order of the elements mean something, so when I group-by I want the result to stay ordered.

2021-03-16T13:08:36.018600Z

The other implication of using an array-map is that if you ever do just about any operation on a large one, it will return an unordered map. e.g. if you do assoc of a new key onto an array-map with at least 9 keys, the return value will not have the same key order guaranteed (and will usually be different)

Aviv Kotek 2021-03-16T13:09:30.018800Z

I see, in my use case I just send the data onwards, so no need

2021-03-16T13:10:07.019Z

If you use an unordered map returned by the current built-in group-by, you can also do a separate function on your input data to calculate the order of keys you want, and create a vector or list of that order. Then you could use that list/vector to decide what order to send key/value pairs from the map onto some next processing step.

2021-03-16T13:10:24.019200Z

Where are you sending the data onwards to, and why does that place need a particular order of keys?

2021-03-16T13:11:36.019400Z

Depending upon how you send it, and how the receiver reads it, if the receiver is using the Clojure reader to read a sequence of characters as a map, for example, then it will also return an unordered map that has forgotten about the order they appeared in the string.

Aviv Kotek 2021-03-16T13:11:52.019600Z

I group-by, then iterate on the result (map) and the sequence result order matters

Aviv Kotek 2021-03-16T13:12:04.019800Z

so basically my output is not the group-by

2021-03-16T13:12:27.020Z

I understand that statement. I am asking what other software is receiving your result that cares about the key order. Is it some other software system you are sending this map to?

Aviv Kotek 2021-03-16T13:12:30.020200Z

but the way I iterate over the group-by order matters (as map idx0 result should not change to the original item)

Aviv Kotek 2021-03-16T13:12:36.020400Z

yes

2021-03-16T13:12:46.020600Z

Did you write that receiving software system?

2021-03-16T13:12:50.020800Z

Is it written in Clojure?

Aviv Kotek 2021-03-16T13:12:52.021Z

nope

Aviv Kotek 2021-03-16T13:13:06.021200Z

react UI which renders the array I send to (in given order)

2021-03-16T13:13:23.021400Z

Are you sending the data as JSON, or something else?

Aviv Kotek 2021-03-16T13:14:43.021600Z

json yep

2021-03-16T13:14:49.021800Z

Sorry if I'm asking a lot of questions, but realize that the situations where key order in a map actually matters are fairly small in the Clojure world, and the order is easily lost at many many different places. I ask because if I tell you how to preserve key order in this one little place, I worry that the very next place you send it to might just forget it.

Aviv Kotek 2021-03-16T13:16:53.022Z

basically I have DB records with timestamp + common column. I select from DB ordered by creation_date and group-by that column, then do some map iteration then in my result I expect the vector to have same result as the order of the db fetch

2021-03-16T13:16:53.022200Z

Also, now that I look at the implementation of group-by, it frequently looks up keys in the intermediate map it is building up, so if you try to create a group-by-return-array-map that starts with an empty array-map and builds up the result one key/value pair at a time, it will do linear time lookups for every key/value pair in the input.

2021-03-16T13:17:34.022400Z

so O(N^2) time algorithm doing it that way.

Aviv Kotek 2021-03-16T13:18:40.022600Z

hmmms

Aviv Kotek 2021-03-16T13:19:14.022900Z

not good then

Aviv Kotek 2021-03-16T13:19:43.023100Z

https://stackoverflow.com/a/15500064/8162407

2021-03-16T13:19:45.023400Z

If you want to do this faster, then one way is to write a new function that is similar to group-by that keeps an unordered map as an intermediate value inside, and also a vector that remembers the order of keys you want. Return both things.

Aviv Kotek 2021-03-16T13:19:52.023600Z

did some trick here, not sure I got his point

2021-03-16T13:20:34.023800Z

Why do you want to avoid a dependency on the ordering-map library, out of curiosity?

2021-03-16T13:21:01.024Z

Because I think it is pretty darned small, and can help solve this problem efficiently, i.e. no O(N^2) stuff.

Aviv Kotek 2021-03-16T13:22:18.024200Z

cause I think I can get around the group-by and use something else

Aviv Kotek 2021-03-16T13:22:48.024400Z

I might reduce the ds and get same result as the group-by

2021-03-16T13:23:07.024600Z

If you need an actual map to write as JSON, i.e. it cannot be a list/array/whatever-JSON-calls-its-ordered-sequence-thing, then either you need to have a JSON-writing-library that lets you specify the order of keys separately from a map somehow, or you need a Clojure map-like thing that lets you control the key order completely.

2021-03-16T13:23:52.024800Z

FYI, as far as I have heard, your are also dealing with code that relies on ordering of keys in a JSON map, violating either the letter and/or spirit of the JSON spec.

Aviv Kotek 2021-03-16T13:24:08.025Z

why not just reduce to an ordered set?

Aviv Kotek 2021-03-16T13:24:26.025200Z

reduce as to do the group-by job

2021-03-16T13:24:47.025400Z

Is the order you want for the keys defined by some simple comparison function, e.g. increasing numerical order?

2021-03-16T13:24:53.025600Z

If so, a sorted-map does the job.

Aviv Kotek 2021-03-16T13:25:15.025800Z

order relies on the index of the elements

2021-03-16T13:25:18.026Z

If you want some arbitrary custom order of keys, then you could also write a custom comparator function that compares key values in that order.

Aviv Kotek 2021-03-16T13:25:42.026200Z

[m1, m2, m3,....mn] some might have key to group-by on but the result map should be in same order as the array indexes

Aviv Kotek 2021-03-16T13:26:12.026400Z

alright, i'll try something, i'll update here:pray:

Aviv Kotek 2021-03-16T13:26:14.026600Z

thanks!!

2021-03-16T13:28:13.026800Z

e.g. if you have keys "foo" "bar" "baz" and you have some arbitrary order that you can define as a Clojure map to relative numeric values, e.g. {"foo" 1, "bar" 2, "baz" 3}, then you can use that map to create a custom comparator function, and create a sorted-map that uses that comparator function, which will sort the keys in the arbitrary order you want.

👍 1
Daniel Stephens 2021-03-16T13:59:03.027700Z

thought I'd mention partition-by , I can't exactly tell how you are using the output of the group-by, so if you specifically need the keys from it then partition-by might not help, but my read of the situation is that you want to group up an already ordered collection, each time a value changes.

Aviv Kotek 2021-03-16T14:04:34.027900Z

yes! that's actually all I needed, nice!

Aviv Kotek 2021-03-16T14:05:30.028100Z

it will group-by each time to a seq in iteration order, then i'd iterate it the same way i'd iterate the k/v map of the group-by (that's actually what I needed), great!

🎉 1
2021-03-16T15:22:49.029400Z

glad you found what you were looking for in the first place. Sorry if I distracted you with issues on the thing you didn't quite want 🙂

Aviv Kotek 2021-03-16T15:42:29.029600Z

nah, had the chance to look on some clojure impl code, thanks bud!!

athomasoriginal 2021-03-16T21:49:17.032900Z

I have a dev/user namespace. Things are working well. I have added a few dev tools like user/dbg. What are some of the approaches for configuring the REPL so I can reference user/dbg as just dbg in other namespaces without having to run require or use in each namespace where I would like to access them?

cschep 2021-03-16T22:26:18.034700Z

i’m using an atom to a map, atom of a map? how do you even say that. 🙂 I’m using an atom referencing a map as a database for a tiny app i’m working on. It holds data about baseball players. I have been writing some functions to do data access / updates.

cschep 2021-03-16T22:26:29.035100Z

(defn update-player
  [id key value]
  (swap! league
         (fn [current-state]
           (assoc-in current-state [id key] value))))

cschep 2021-03-16T22:26:40.035400Z

this seems to work great, but it prints the entire database in the repl everytime

cschep 2021-03-16T22:27:08.036100Z

maybe a success or fail would be good or silent success error on fail.. does swap! ever fail? or.. how would silence its output?

2021-03-16T22:27:36.036700Z

Regarding terminology, I think "an atom containing a map" is fairly common.

cschep 2021-03-16T22:27:42.036900Z

containing! nice.

2021-03-16T22:27:52.037100Z

"referencing" is clear, too

2021-03-16T22:29:29.038200Z

In a REPL, any time any function can return a value that is very large in being printed, you can do something like (def x1 (some-expression-returning-big value)) , and it will only show #'user/x1 instead of the big value. The big value will be stored in x1 for future reference/manipulation.

2021-03-16T22:30:16.038700Z

Unless the function you pass to swap! can throw an exception, I don't know of any reasons why swap! can fail

2021-03-16T22:30:32.039200Z

Even then, swap! should leave the current value contained in the atom there.

cschep 2021-03-16T22:30:48.039400Z

right that makes sense

cschep 2021-03-16T22:31:08.039800Z

it seems to be updating the atom AND returning the new value

cschep 2021-03-16T22:31:43.040600Z

Atomically swaps the value of atom to be:
(apply f current-value-of-atom args). Note that f may be called
multiple times, and thus should be free of side effects.  Returns
the value that was swapped in.

2021-03-16T22:31:44.040700Z

That is what the last sentence of the doc string printed by (doc swap!) says it should do.

cschep 2021-03-16T22:31:52.041Z

ha ha nice, thanks for the pointer

2021-03-16T22:32:26.041500Z

See also swap-vals! for its different, and sometimes useful, return value

cschep 2021-03-16T22:34:13.042100Z

that’s cool, both the old and new

cschep 2021-03-16T22:35:50.043600Z

i wrapped the swap! call in a count call to return far less info, is that something people do like, maybe i could wrap it in do and return nil?

2021-03-16T22:35:53.043700Z

I don't know of any way to automatically do that off the top of my head, but if you have an editor or IDE where you have a key sequence of your choice assigned to "evaluate the expression just before the cursor", so you don't have to type the same expression more than once, it can be good to have frequently used expressions like `(require 'user.dbg)` or `(use 'user.dbg)` in a file at the top level of your project, e.g. named something like `scratch.clj`, that you often keep open while working in that project.

2021-03-16T22:36:00.043900Z

^^ @tkjone

2021-03-16T22:36:46.044800Z

A Clojure function body is already implicitly wrapped in a do, without having to type (do ...), so you can make the last expression nil in the body if you always want to return nil

2021-03-16T22:37:50.046Z

If you aren't worried about large values accidentally being printed at a REPL, in development it can often be nice when functions return a useful value, even if you do not always use it. But yes, I see people sometimes returning a value like nil when they explicitly want that.

cschep 2021-03-16T22:38:45.046300Z

oh that’s really useful thanks, i didn’t know about the implicit do

2021-03-16T22:38:51.046400Z

Having your chosen editor/IDE set up so that a very convenient short key sequence can send expressions to your REPL for evaluation is HIGHLY RECOMMENDED

cschep 2021-03-16T22:38:53.046800Z

(defn update-player
  [id key value]
  (swap! league
         (fn [current-state]
           (assoc-in current-state [id key] value)))
  (str "updated " id))

cschep 2021-03-16T22:38:55.047Z

this is kinda useful

cschep 2021-03-16T22:39:03.047300Z

but yeah who knows maybe having the whole value there is good

cschep 2021-03-16T22:39:06.047500Z

anyway, thanks!

👍 1