Fork me on GitHub
Juan Flores14:01:03

Hi all, I started to play around with Clojure and I love itΒ πŸ™‚, looking for some help/direction in the following: I have the following sequence and want to get all the :id values from it (including the nested maps):

({:id "A123",
  :x  ({:id "B123", :name "a", :y ()})}
 {:id "A454",
  :x  ({:id "B546",
        :y  ({:id "C888", :name "b"}
             {:id "C999", :name "b"}
             {:id "C444", :name "b"})}
       {:id "B774",
        :y  ({:id "C123", :name "b"})})})
I want to get
("A123" "B123" "A454" "B546" "C888" "C999" "C444" "B774" "C123")
I got to the solution using map, flatten, and keep but wanted to see if someone thinks of a better/cleaner way of doing it :)

Ben Sless14:01:16

Flatten is never clean. It's better to use tree-seq


Here's a postwalk solution:


> I got to the solution using map, flatten, and keep but wanted to see if someone thinks of a better/cleaner way of doing it πŸ™‚ Mind sharing your solution?


tree-seq to the rescue (thanks @UK0810AQ2):

 (tree-seq map?
           (fn [m]
             (concat (:x m)
                     (:y m)))
           (quote {:id "A454",
                   :x  ({:id "B546",
                         :y  ({:id "C888", :name "b"}
                              {:id "C999", :name "b"}
                              {:id "C444", :name "b"})}
                        {:id "B774",
                         :y  ({:id "C123", :name "b"})})}))
 (map :id))
;; => ("A454" "B546" "C888" "C999" "C444" "B774" "C123")
Works when called on a node. You have a toplevel sequence, so you'll either have to create a fake toplevel node {:x (... and remove nils afterwards, or do the toplevel step manually.

Ben Sless16:01:36

I'd do it with eduction. Will send an example in a bit

Ben Sless17:01:50

(defn go
    (string? o) [o]

    (sequential? o) (->Eduction (mapcat go) o)

    (map? o)
    (let [{:keys [id x y]} o]
      (->Eduction (mapcat go) [id x y]))))

πŸ‘€ 1
Juan Flores17:01:03

This is great, really appreciate your help! There is a lot to learn from these solutions, it will take some time to fully understand them but I think it's an excellent starting point. @U3X7174KS My solution was wrong but I was doing something like this, it didn't look right at all

(defn go
  (->> inventory
       (map :x)
       (keep :y)
       (keep :id)))

πŸ‘ 1
Ben Sless18:01:52

I often find that flatten is a code smell. Don't think I came across a case where it should have been used, yet. Whenever you're doing something recursive (walks are recursive), start from your terminals and build up

❀️ 1
Ben Sless18:01:13

You don't necessarily have to get it right the first time, either. In my implementation I forgot to cat in the sequential case, but it's easy to see in the output

πŸ™‚ 1

even simpler tree-seq version:

(->> input
    (tree-seq coll? seq)
    (keep :id))

πŸ™Œ 4

literally all we need here is "recursively, if it has an :id key, then the val for that key", and tree-seq makes that easy

πŸ’― 6

99% of the time, if I am using tree-seq, the first two args are coll? and seq

πŸ™Œ 1

Is there something like jet but with YAML support?


babashka ;)


:rolling_on_the_floor_laughing: why jet then?


jet precedes babashka


oh! I didn't know that.


Has anyone leveraged carmine the redis client within the context of stuart sierra's component framework? I'm wondering what a component that contains a redis connection pool might look like.


I have something like

(defrecord Redis [redis-spec datasource]
  (start [component]
    (if datasource
      (let [redis (assoc component :datasource redis-spec)]
        (car/wcar (:datasource redis)
  (stop [component]
    (assoc component :datasource nil)))
at the moment, but I am not sure how the wcar method manages this thread pool. The redis spec I provide has a :pool {} which is stored under :datasource in my record and is used in the start method's usage of car/wcar . Iam not sure how to keep this thread pool "open". The docs I've seen so far all interact with redis through the wcar macro which apparently should leverage the same thread pool, but I am not understanding how this works or is kept open. Is my :datasource map being modified by the macro?


if I recall, rather annoyingly, carmine doesn't let you manage connection objects, but instead keeps its own global reference to them


so your car/ping will start up the connection, and the carmine library will keep it open


I wrote a custom connection pool thing using jedis directly at work. jedis comes with some kind of built in connection pool, we had some requirements around handling long running pub/sub connections I wasn't sure it met


Thanks. I was about to ask if it was possible to manage my own connection then pass that to carmine somehow. That is what I am doing with my database following sean corfield's web app example with a pooled postgres connection


yeah that is kinda annoying or unexpected isn't it


We've gone through a number of Redis solutions along the way, discarding several for various reasons, before settling on basic Jedis plus our own connection pooling code. We did use Carmine at one point before switching to Redisson but I no longer remember what my issues were with Carmine (certainly it managing its state in globals internally would have been one of the things that I decided we couldn't live with).


Redisson is bloatware (and was very slow to provide JDK compatibility post Java 8, as I recall) -- we shaved 10-15MB off our JAR files by dropping Redisson I think?


I used to be quite a fan of "fancy" Clojure wrappers for Java libraries but over the years I've switched to being more a fan of just using an industry-tested Java library with some interop πŸ™‚


Jedis looks pretty simple to set up at least at my basic-needs level as well I may just switch to that