Fork me on GitHub
#beginners
<
2023-02-14
>
M J08:02:08

I have an array of objects, which have :group_id as keys inside, and want to separate them based on :group_id. I did this: (sseq (group-by :group_id array)) , Now I want to run a function that returnsa component for each item, like this is my result: ([1 [:name "Name 1" :group_id 1] [:name "Name 2" :group_id 1]], [2 [:name "In group 2" :group_id 2]]) I want a map that goes over them, and returns a component of divs containing the names, each group by itself. I have an array of objects which have :group_id as a key, I did

Ferdinand Beyer08:02:00

> I have an array of objects, which have :group_id as keys inside Do you mean you have a vector of maps? Like this:

(def data [{:group_id 1, :name "One"}
           {:group_id 1, :name "Uno"}
           {:group_id 2, :name "Two"}])
> Now I want to run a function that returnsa component for each item Since you are talking about components and divs, are you building a web application? What library are you using? What “item” are you referring to? When you use group-by, you will get a map of vectors, keyed by the :group_id. Do you want to create a component for each group?

M J08:02:37

yes exactly

Ferdinand Beyer08:02:31

Something like this?

(for [[group members] (group-by :group_id data)]
  [:div
    [:span "Group ID: " group]
    [:ul
      (for [{:keys [name]} members]
        [:li "Member: " name])]])

Ferdinand Beyer08:02:05

Are you using Hiccup? Reagent? Something else?

Ferdinand Beyer08:02:09

Then this should work, but don’t forget that you need a :key for each item when you loop in React

M J09:02:57

Is there any way I could transform this to maP-INDEXED INSTEAD OF A FOR?

Ferdinand Beyer09:02:08

(->> data
     (group-by :group-id)
     (map-indexed (fn [i [group-id members]]
                   [:div ,,,])))

Ferdinand Beyer09:02:02

I think I would split your problem into three components/functions, so that you have one component for the group and one for the member.

M J09:02:51

Sorry its not working.. maybe Im missing something. The whole reason I wrote in beginners is Im new to it..

M J09:02:14

Can you please write it like before in details so that I make sure it works and go with it?

Ferdinand Beyer09:02:34

(map-indexed
 (fn [i [group-id members]]
   [:div {:key i}
    [:span "Group ID: " group-id]
    [:ul
     (map-indexed
      (fn [k {:keys [name]}]
        [:li {:key k}
         [:span "No. " k " - " name]])
      members)]])
 (group-by :group-id data))

pavlosmelissinos09:02:41

(If you're a beginner why would you want to start with reagent? The web has many moving parts, perhaps try something simpler, make a tictactoe game or something to learn the language first)

M J09:02:24

Im working o a project at work that's already built..

Ferdinand Beyer09:02:30

@U03KB1C9N8M — is that example clear to you? It is probably important that you do understand the syntax, for, map-indexed, and how the examples here use them.

M J09:02:42

YEs, understood. But group id is nil..

M J09:02:00

It doesnt divide them each to its own group id like before

Ferdinand Beyer09:02:33

What’s your code?

M J09:02:49

Your last code exactly

Ferdinand Beyer09:02:13

Does it contain the edited group-by ?

Ferdinand Beyer09:02:29

How does your data look?

Ferdinand Beyer09:02:31

I think it is important that you divide and conquer. Split your problem into smaller ones.

M J09:02:30

Group ID: • No. 0 - One • No. 1 - Uno • No. 2 - Two

Ferdinand Beyer09:02:16

I did run the code:

(let [data [{:group-id 1, :name "One"}
              {:group-id 1, :name "Uno"}
              {:group-id 2, :name "Two"}
              {:group-id 3, :name "Three"}
              {:group-id 3, :name "Tres"}]]

    (map-indexed
     (fn [i [group-id members]]
       [:div {:key i}
        [:span "Group ID: " group-id]
        [:ul
         (map-indexed
          (fn [k {:keys [name]}]
            [:li {:key k}
             [:span "No. " k " - " name]])
          members)]])
     (group-by :group-id data)))
Output:
([:div
  {:key 0}
  [:span "Group ID: " 1]
  [:ul ([:li {:key 0} [:span "No. " 0 " - " "One"]] [:li {:key 1} [:span "No. " 1 " - " "Uno"]])]]
 [:div {:key 1} [:span "Group ID: " 2] [:ul ([:li {:key 0} [:span "No. " 0 " - " "Two"]])]]
 [:div
  {:key 2}
  [:span "Group ID: " 3]
  [:ul ([:li {:key 0} [:span "No. " 0 " - " "Three"]] [:li {:key 1} [:span "No. " 1 " - " "Tres"]])]])

M J11:02:04

Thanks alot. whe I created the data, I had :group_id with underscore :man-facepalming:

sarna12:02:28

hey, I can't figure out how to make sessions work in ring. I have something really simple right now:

(defn foo-handler [req]
  (let [session (:session req)
        session (assoc session :credentials "secret")]
    (-> (redirect "/bar")
        (assoc :session session))))

(defn bar-handler [req]
  (let [session (:session req)]
    (html [:div
           [:p "hello from bar!"]
           [:p (str "session is: " session)]])))

(defroutes app
  (GET "/foo" request (foo-handler request))
  (GET "/bar" request (bar-handler request))
  (compojure.route/not-found (html [:p "Page not found"])))

(defonce server (atom nil))
(http.server/run-server #'app {:port 8080})
I tried debugging it, and it seems like I'm returning a correct map from foo. somehow, though, :session gets lost somewhere before getting to bar - in the request bar sees, :session is nil. how come?

delaguardo13:02:18

compojure doesn't know about session out of the box. you need some middleware that can handle mutable session store to persist data between requests. https://ring-clojure.github.io/ring/ring.middleware.session.html this comes with ring library and can handle simple case

rolt13:02:25

normally you have a middleware that persist the session and set the session key in the cookies, so that the second request works

sarna13:02:21

ohhh I need to plug in the middleware. knew that I was missing something basic. thank you! :)

James Amberger15:02:25

How can I terminate a function in the repl (say run-jetty for example) without losing the repl?

2
ghadi15:02:02

you don't

😜 2
ghadi15:02:24

if there is a function that doesn't return, you can run it in a background thread (using future or similar)

ghadi15:02:06

I'm not sure where run-jetty is from, but lots of http servers have knobs not to tie up the foreground thread

ghadi15:02:52

i thought that looked familiar. Look at :join? false

practicalli-johnny17:02:32

You can terminate, stop start the jetty process without killing the repl very easily. Ideally a reference to the server process should be created. This reference can be used to stop the jetty server. This can be done as a separate expression https://practical.li/clojure-web-services/app-servers/simple-restart.html Or using an atom to hold the reference https://practical.li/clojure-web-services/app-servers/atom-based-restart.html Or when there are multiple components, then use mount, integrant, donut or one of the other lifecycle libraries

James Amberger19:02:01

@U05254DQM The procedure in your first link doesn’t seem to be working the way I expect. My repl is still blocked, whether it’s the terminal repl or my editor’s connection to the nrepl.

James Amberger19:02:03

(defn -main [& [port]]
  (when-not nrepl (start-nrepl))
  (run-jetty
      (-> handle-dump
          (ring.middleware.file/wrap-file "static")
          wrap-proxy)
      {:port (or port 8080)}))

ghadi22:02:12

@U032EFN0T33 see what i wrote

ghadi22:02:41

Ignore other suggestions until you try what I said

practicalli-johnny22:02:57

Jetty keeps the process, so the repl promt is blocked. Use :join? false as an option

2
practicalli-johnny22:02:48

This option returns the process control back to the REPL prompt. This option is in both examples shared

James Amberger14:02:22

ah so it is. Thanks.

Matthew Twomey17:02:36

Can I define a function in another namespace using defn “directly”, without changing namespace first?

ghadi17:02:09

are you typing stuff into the repl manually?

Matthew Twomey17:02:35

Yes, but my goal is to add a convenience method to a 3rd party library.

Matthew Twomey17:02:50

(for use in a particular script of mine)

ghadi17:02:01

why not define that convenience method in your own ns?

Alex Miller (Clojure team)17:02:28

you can with intern (but not with defn) - but I agree with Ghadi this is generally not advisable

Matthew Twomey17:02:26

In this case, I’m doing this mainly because it’s essentially a “missing method” from this library. It’s one of many others that all act in the same manner. I also plan to PR it once I’m certain.

Matthew Twomey17:02:04

Thanks though - confirming what I was seeing, I just wasn’t sure if I was missing some way.

phronmophobic18:02:34

Not sure it's a good idea, but you could use something like https://github.com/clojure/clojure-contrib/blob/a6a92b9b3d2bfd9a56e1e5e9cfba706d1aeeaae5/modules/with-ns/src/main/clojure/clojure/contrib/with_ns.clj#L20

(defmacro with-ns
  "Evaluates body in another namespace.  ns is either a namespace
  object or a symbol.  This makes it possible to define functions in
  namespaces other than the current one."
  [ns & body]
  `(binding [*ns* (the-ns ~ns)]
     ~@(map (fn [form] `(eval '~form)) body)))


(with-ns 'user
  (defn foo [])) ;; #'user/foo

Matthew Twomey18:02:31

That’s funny, I also had “with-ns” on my brain - primarily because elisp tends to use this pattern with a lot of things.

phronmophobic18:02:28

For your particular use case, I usually clone the repo locally and use the library as a local dep. For my setup (emacs+cider), that let's me jump to namespace and edit/evaluate the code normally. When I'm ready, I can then submit a pull-request upstream.

Matthew Twomey18:02:15

Yeah, I will get to that as well. This was just some first testing and I was surprised that I couldn’t intern or def it directly.

joshcho17:02:55

I have a server up and running with jetty. Right now if something goes wrong in my route handler, I can't see it in the cider repl or nrepl. How do I see this information (runtime errors)?

hiredman18:02:51

depends, but absent other factors errors are likely being printed out to the stdout or stderr (I forget which) of the java process

hiredman18:02:00

there are ring middlewares that do things like render the error as html and send it as the http response https://github.com/ring-clojure/ring/blob/master/ring-devel/src/ring/middleware/stacktrace.clj#L108-L122

👍 2
hiredman18:02:08

there are also things people do (and maybe something you can setup in cider, not sure) that will try and redirect System/out (by default the jvm processes stdout) to output in the repl

Jakub Šťastný18:02:02

Having a custom type, how can I define equality comparison? As in is there a way of extending = to work with my type? Something like (= person-obj1 person-obj2) I'm still rather confused about all these, but it seems to me that = is not a multi-method, so is there no way of extending it then? Then how do you guys compare custom objects?

dpsutton18:02:57

(deftype Foo [x]
    Object
    (equals [this other]
      (cond (identical? this other) true
            (instance? Foo other) (= x (.x other))
            :else false)))

  (= (->Foo 3) (->Foo 4))
  (= (->Foo 3) (->Foo 3))

Jakub Šťastný18:02:59

Thanks @U11BV7MTK. How can I found about these though? If I go to https://clojuredocs.org/clojure.core/= this is not mentioned. There are other overrides like this toString for str etc, but how do I find out about them, is there any list? As it doesn't tend to be in the docs.

dpsutton18:02:47

>

Same as
> Java x.equals(y)
>

Frank Henard18:02:04

The Clojure way is to not use types, but to use maps (just data). (= {...} {...}) will work

dpsutton18:02:11

(doc =)
-------------------------
clojure.core/=
([x] [x y] [x y & more])
  Equality. Returns true if x equals y, false if not. Same as
  Java x.equals(y) except it also works for nil, and compares
  numbers and collections in a type-independent manner.  Clojure's immutable data
  structures define equals() (and thus =) as a value, not an identity,
  comparison.

dpsutton18:02:18

so i would read about equals

dpsutton18:02:54

but it does get confusing and you have to reference java’s notion of equality

dpsutton18:02:07

but that’s the pointer that it’s using java’s equals

Jakub Šťastný20:02:25

@U064X3EF3 thanks, I'll read up on that and see if I can report an issue.

Noah Bogart19:02:50

i know micro benchmarks are just a game and "don't optimize until your application is actually slowing down", but on the other hand... if I am going to access a value from a hashmap multiple times, is it better to assign in a let block and reference the binding or to just use the keyword to access every time?

practicalli-johnny19:02:06

I always find the best way is to measure. Wrapping an expression with the (time ,,, ) function will give a very rough guide and quickly pick out the largest disparities in time https://clojuredocs.org/clojure.core/time will produce more consistent results for an expression

practicalli-johnny19:02:39

I always use a let block rather than destructure the same thing multiple time, but for cleaner code (usually) than for performance concerns

👍 2
pppaul19:02:31

you aren't talking about micro benchmarks. likely the compiler will optimize this issue you describe, but your code will be worse off in terms of maintenance and readability. a lookup in a hash map doesn't tell you much, so you should document that, and an easy way of documenting something like that is to give it a name.

👍 2
seancorfield19:02:37

If I use a hash map value multiple times in a block of code, I'll tend to lift that out as a local binding but not for performance -- for readability, in terms of saying "this particular key value is important enough to this block of code that I'm giving it a name".

👍 4
Noah Bogart19:02:55

Yeah, I tend to prefer it too. Just had the stray thought and couldn't figure out a way to test it in isolation

seancorfield19:02:19

If you happen to access the same value multiple times but it really isn't important that you're doing that, don't bother putting it in a let -- it is just noise at that point.

seancorfield19:02:57

It certainly isn't worth timing it or persuading yourself you're doing it for performance reasons 🙂

seancorfield19:02:04

Repeated whole subexpressions that take a non-trivial amount of time to compute? Sure, evaluate them once and give them a name. Repeated (:k m) expressions? Not worth it unless naming them is actually important/useful.

👍 2
kennytilton19:02:19

This will not be a performance issue, @UEENNMX0T, just a readability issue. As for the let, don't forget destructuring like {:keys [frequent-ref other-ref ....]}, especially if the map is a function parameter. In the latter case, we can avoid the let and the noisy repeated access.

👍 2
Ben Sless20:02:40

It depends on your problem domain. If your performance budget is measured in hundreds of NS, then worry about it. Otherwise worry about readability. The longer answer is that lookup has a few ns overhead, depending on the lookup method and target. There's also the issue of call site polymorphism which affects JIT optimization

mister_m22:02:46

is it possible to specify a java ThreadGroup for threads created through calling future? I was trying to profile my little program to ensure that threads were being closed but it is difficult to tell what thread is the right one to look at.

hiredman22:02:54

threads can't be closed

hiredman22:02:12

future may create a thread or may reuse an existing idle thread

mister_m23:02:44

I see. I'm not super familiar with the java thread model. I've got some reading to do.

mister_m23:02:31

I am writing a little socket server currently, and I have a start function that returns a future . The body of the future loops and checks a running? flag to determine whether or not the socket continues to accept connections (and the thread does not go back to the JVM). It doesn't feel very good to write because the future body is creating a closure with the stateful arguments to this start function.

mister_m23:02:05

(the stateful arguments being a map within which is where actual SocketServer object gets placed)

hiredman23:02:11

sockets are by their nature generally stateful things

jumar04:02:00

I wouldn't use Thread Groups for that anyway. You can simply set a name for a given thread when the future starts. We use something like this in our code to give threads a meaningful name while a block of code is executed

(defmacro with-thread-name-suffix
  "Appends `thread-name-suffix` to the current thread name (separates them with \"___unix-time-millis_<currentTimeInMillis>__\"),
  executes the body, and then restores the original thread name.
  This can be useful for debugging - by temporarily setting thread name to something meaningful."
  [thread-name-suffix & body]
  `(let [current-name# (.getName (Thread/currentThread))]
     (.setName (Thread/currentThread)
               (str current-name# "___unix-time-millis_" (System/currentTimeMillis) "__" ~thread-name-suffix))
     (try
       ~@body
       ;; restore the original thread name
       (finally (.setName (Thread/currentThread) current-name#)))))