Fork me on GitHub

What’s the easiest way to dissoc-in, or to transform a value to not being there?


ideally, I’d like to be able to navigate to a value using walker, and then just setval to nothing, not nil, but nothing, is that possible?


@conaw: Perhaps navigate one step behind using selected? and then transform with dissoc. Still not elegant though


looks like this is the closest we’ve got right now to what I’m looking for


so, heres where I’m stuck. right now I’m able to filter out a given value from a vector that is nested one level deep, I’m trying to figure out how to do this recursively though so I could go as deep as needed. the following works

(defpathedfn remove-x [x]
  (filterer #(not= x %)))

(defn t3 [v a]
  (select [
           (remove-x a)
           (if-path vector?
                    (remove-x a)
                    STAY)] v))

(deftest test1
  (testing "removal"
     (= [1 2 3 [4]]
        (t3 [1 2 3 [4 5] 5] 5)))))
what I want is (= [1 2 3 [4 [6]]] (t3 [1 2 3 [4 [5 6] 5] 5] 5))


@conaw: here's one way to do it


(defn remove-x [x]
  (fn [v]
    (filterv #(not= x %) v)))

(declarepath AllVectors)
(providepath AllVectors
  (if-path vector?

(transform AllVectors (remove-x 5) [1 2 3 [4 [5 6] 5] 5])


you could also use ALL-ELEM-SEQ from that linked github issue in conjunction with AllVectors


which would look like: (setval [AllVectors ALL-ELEM-SEQ #(= 5 (first %))] nil [1 2 3 [4 [5 6] 5] 5])


I'm attempting to "shake" a map of all top level keys where where a deeply nested key in each branch matches a predicate. Is that something I would tackle with transform? Or would I use select in combination with filterer?


what's an example of the input/output you're looking for?


In this map, I only want to keep top level branches where at least one of the maps in :where have an :op value of "="

{:k1 {:where [{:op    "="
               :value "A"}
              {:op    "ignore"
               :value "X"}]}
 :k2 {:where [{:op    "<"
               :value "A"}
              {:op    "lookup"
               :value "C"}]}
 :k3 {:where [{:op    "="
               :value "A"}
              {:op    "lookup"
               :value "E"}]}}

; result

{:k1 {:where [{:op    "="
               :value "A"}
              {:op    "ignore"
               :value "X"}]}
 :k3 {:where [{:op    "="
               :value "A"}
              {:op    "lookup"
               :value "E"}]}}


(k2 doesn't make the cut)


I can build a path to :op no problem, but I lose all parent values when using select. I considered using collect-one at the root of the path while applying filterer at the :op level, but I got the impression I was misunderstanding something fundamental. 🙂


(into {} (traverse [ALL (selected? LAST :where ALL :op #(= "=" %))] data))


you can do it with select too but traverse is more efficient


(doesn't create any intermediate sequence)


I think I overlooked selected? as well.


In your code response, what's the difference between 1) using filterer , or 2) passing an anonymous function to the selection path, as you did?


filterer navigates you to a new sequence


a function acts as a predicate and stops navigation at that point if the predicate is false


for selected?, it acts as a predicate where "true" is when anything is selected


since filterer doesn't stop navigation, that wouldn't be relevant in this case


Okay, that helps a lot. Thanks for the explanation and for solving my use case.


this is worth reading through if you haven't already:


I’m getting "Cannot read property '0' of null” somewhere deep in specter and I wasn’t able to figure out what is going wrong 😞


@darwin: need a reproducible test case


Is it okay to nest selected? functions? Going back to your (into {} (traverse [ALL (selected? LAST :where ALL :op #(= "=" %))] data)) response, what if I wanted to also restrict the selection to the :where vec having a count greater than one?


Oh wait, I'd just use a function there as well.


Oh, double wait, I'm not sure what I just said made sense.


Okay, I think I got it? Two separate selected? expressions:

(traverse [s/ALL
           (s/selected? s/LAST :where #(< 1 (count %)))
           (s/selected? s/LAST :where s/ALL :op #(= "=" %))] data)

Chris O’Donnell14:08:54

@joshkh: I think you could do it with (traverse [ALL (selected? LAST :where #(< 1 (count %)) ALL :op #(= "=" %))] data)

Chris O’Donnell15:08:57

also, if you're traversing a map, you could use MAP-VALS instead of LAST


Ah, that's what how I was thinking about it originally, but I wasn't sure how ALL fit into the path after the function. I guess the obvious answer is that it fits in just fine. 🙂


And thanks for the advice about MAP-VALS. It'll be easier to remember the data structure when I come back to this in the future.

Chris O’Donnell15:08:07

for sure, and it should be a little more efficient, too!


ALL is needed in this case since traverse must emit key/value pairs

Chris O’Donnell15:08:01

good point @nathanmarz. I'm not thinking very clearly today. @joshkh, you can't use MAP-VALS, since MAP-VALS acts the same as [ALL LAST], but you need the ALL outside of selected? and LAST inside it.


I was having working at a big reduce today and I thought, maybe specter will be able to help me. Basically the atomic input is:

[{:timestamp 14424
  :chat-id 2
  :payload "bla"}
 {:timestamp 14421
  :chat-id 1
  :payload "asd"}
  {:timestamp 14400
  :chat-id 1
  :payload "test"}]
I have a list of those that I want to convert to a map indexed by chat-id but keeping the oldest timestamp in the value, as:
{1 {:timestamp 14000 :messages [the list]}
  2 {:timestamp 14424 :messages [the list]}}
Can you folks give me a hint (without full solution is fine) from where to start exploring a solution with specter?

Chris O’Donnell23:08:09

@richiardiandrea: could you give a small example of input/output?


@codonnell: thanks for answering, yes of course, is the above not enough? I am worried it can take too much space

Chris O’Donnell23:08:42

@richiardiandrea: Thanks, that clarifies it.

Chris O’Donnell23:08:07

Would you list just be a list of the payloads for all messages with the given chat-id?


so I need to carry with me the :timestamp


the calculation on the timestamp is a bit mysterious to me at the moment

Chris O’Donnell23:08:09

Does the list of messages need to be in order of timestamp?


(specter newbie)


yes preferably yes