Fork me on GitHub
#specter
<
2017-10-11
>
hkjels08:10:00

I’m starting to get a feel for recursive-path now, but I don’t see how I can solve my problem with it. I want to pluck all the parents of a particular map based on a predicate for that map. The way I see it, I’ll either have to start the search from the leaves of my tree or remove branches as it goes. The latter would require that I keep track of my tree with some local-state, so it doesn’t seem idiomatic to specter?

hkjels08:10:03

This current solution will select the correct maps, but if I try to collect the previous result, that will happen for “bar” as well

hkjels11:10:58

Almost at a solution now

hkjels11:10:41

Can I do a transformation from within a recursive-path macro

hkjels11:10:53

is that frowned upon?

nathanmarz15:10:53

@hkjels not entitely clear on what you're trying to do. Are you just trying to remove the children of all nodes matching a certain name?

hkjels16:10:48

@nathanmarz I’m trying to retrieve all the parent nodes of nodes where the predicate is true and leave out all the children

hkjels16:10:21

of those nodes

nathanmarz16:10:38

@hkjels so in this case you want to navigate to the parents of nodes with name "qux"?

nathanmarz16:10:49

looks like this:

(def data
  {:name "foo"
   :children
   [{:name "bar"
     :children []}
    {:name "baz"
     :children
     [{:name "qux"
       :children
       [{:name "NO!"
         :children
         []}]}]}
    {:name "hello"
     :children
     [{:name "world"
       :children
       [{:name "qux"
         :children
         [{:name "NO!"
           :children
           []}]}]}]}]})

(def NODES
  (recursive-path [] p
    (continue-then-stay :children ALL p)))

(defn nodes-with-child [name]
  (path NODES (selected? :children ALL :name (pred= name)))
  )

(select (nodes-with-child "qux") data)

nathanmarz16:10:15

easiest way to do a recursive navigation is first make a path that navigates to every node, then do whatever filtering you need on top of that

hkjels16:10:56

Nice tip! There’s still the issue where children of the selected node is in the structure

nathanmarz16:10:07

if you want the results without children then specify that in your selection

nathanmarz16:10:54

(select [(nodes-with-child "qux") (transformed :children (constantly NONE))] data)

hkjels18:10:48

That short-circuits the walk on the node prior to the one where the predicate is true

Chris Dewar-English16:10:11

I want to cherry-pick a set of values from different parts of a nested structure. Is that a reasonable thing to try to do with Specter?

Chris Dewar-English16:10:27

I currently use get-in and map over the keywords to get to each value. It works fine, but what is the principle behind doing that with specter?

nathanmarz16:10:36

if you want to get all the summaries, it's (select [(keypath "issues") ALL (keypath "fields" "summary")] data)

Chris Dewar-English16:10:48

I would like to pull multiple values, i.e. from issues/id, issues/key, issues/fields/summary, issues/fields/project/name

Chris Dewar-English16:10:17

I'm not sure all those selects make sense in a single navigator do they?

nathanmarz16:10:21

how do you want the results structured?

Chris Dewar-English16:10:05

I'm essentially trying to flatten the JSON data into CSV.

Chris Dewar-English16:10:40

And cherry-pick just the values I need.

nathanmarz16:10:59

(select [(keypath "issues") ALL (collect-one (keypath "id")) (keypath "fields") (collect-one (keypath "summary")) (keypath "project" "name")] data)

nathanmarz16:10:27

that will get everything you need as a vector in that order

Chris Dewar-English16:10:43

Ok, so drill down to the end element, but collect the values as you go?

nathanmarz16:10:44

if you're doing a lot of navigation by string keys, it may be worth creating an implicit navigator for strings as is done for keywords https://github.com/nathanmarz/specter/blob/master/src/clj/com/rpl/specter.cljc#L1161

nathanmarz17:10:02

then you can write (select ["issues" ALL (collect-one "id") ...

Chris Dewar-English17:10:52

That part is ok, the string keys are turned into keywords using Cheshire when it parses

Chris Dewar-English17:10:31

Regarding using collect... I've just assumed collect-one was only applicable when doing a transform. I didn't realise it would just contain the final elements!

nathanmarz17:10:53

yep, that's the only behavior that makes sense for select codepath

Chris Dewar-English17:10:44

Good stuff. Thank you, Nathan.

gdeer8121:10:18

okay, I think I've found an interesting data structure for learning; koan 4 will be about writing transformations to facilitate a space-shooter game. this makes the examples like this easier to read (transform [ALL (collect-one :velocity) #(:enemy? %) :y] (fn [velocity current-y] (* velocity current-y)) entities)

gdeer8121:10:53

this transformation obviously takes all the entity maps and updates every enemy's y coordinate by its velocity

gdeer8121:10:36

that can process about 5k maps in sub-miliseconds (caveat: i'm using time as my micro-benchmark tool)

nathanmarz22:10:30

@gdeer81 cool, that sounds nice and tangible

gdeer8122:10:21

differences in decisions about how you want to manipulate the game objects easily illustrate subtle differences in navigations

michaelwfogleman22:10:23

@nathanmarz: Perhaps what you are thinking of is using re-seq? (re-seq #"." "abc") ("a" "b" "c")

michaelwfogleman22:10:35

(re-seq #"abc+" "abc") ("abc")

michaelwfogleman22:10:03

going to try and poke at the transform