specter

2025-06-26T06:17:35.241319Z

Hello 👋 I've encountered the following behavior:

(sp/select (sp/walker #(and (map-entry? %)
                            (= (key %) :id)
                            (keyword? (val %))))
              data) =>
[[:id :map-feed]
 [:id :feed]
 [:id :cart]
 [:id :checkout]
 [:id :reservation]
 [:id :section]
 [:id :app-menu-links]
 [:id :chat]
 [:id :entity-viewer]
 [:id :orders-feed]
 [:id :messages]
 [:id :404-error]
 [:id :banner]
 [:id :form]
 [:id :map]
 [:id :catalog]
 [:id :static-info]
 [:id :catalog-row]]
but transform doesn't hit anything:
(sp/transform (sp/walker #(and (map-entry? %)
                              (= (key %) :id)
                              (keyword? (val %))))
             (fn [[k v]] (print [k v]) [k (name v)])
             data) => same data + no prints
do select and transform behave differently for walker?

nathanmarz 2025-06-26T21:22:29.281069Z

no, they don't

nathanmarz 2025-06-26T21:22:43.725149Z

what is the value of data where you're seeing a difference?

2025-06-27T05:37:37.814199Z

{"map"
   {:description "A collection of entity presented on a map view, by their geo location",
    :complexity 1,
    :capabilities
    [{:id "view_{posts}"}
     {:id "view_{user}_{posts}"}
     {:id "view_{group}_{posts}"}
     {:id "create_{posts}"}
     {:id "comment_{posts}"}
     {:id "rate_{post}"}
     {:id "share_{post}"}
     {:id "view_{products}"}
     {:id "view_{user}_{products}"}
     {:id "view_{business}_{products}"}
     {:id "view_{group}_{products}"}
     {:id "add_{products}_to_cart"}
     {:id "search_{products}"}
     {:id "search_{posts}"}
     {:id "search_{users}"}
     {:id "filter_by_tags_{products}"}
     {:id "filter_by_tags_{posts}"}
     {:id "view_{users}"}
     {:id "follow_{user}"}
     {:id "request_friendships"}
     {:id "rate_{users}"}
     {:id "view_{businesses}"}
     {:id "view_{user}_{businesses}"}
     {:id "manage_{users}"}
     {:id "manage_{products}"}
     {:id "manage_{businesses}"}
     {:id "manage_{posts}"}
     {:id "navigate_to_{page}"}],
    :type "feature",
    :id "map"},
   "banner"
   {:description
    "A promotional strip that draws attention to important information or offers. Usually located on the top part of the page",
    :complexity 1,
    :capabilities
    [{:id "view_{segment}"} {:id "view_{image}"} {:id "view_{text}"} {:id "view_{button}"} {:id "navigate_to_{page}"}],
    :type "feature",
    :id "banner"},
   "404-error"
   {:description "A default fallback view that appears when the user request does not found",
    :complexity 1,
    :capabilities [{:id "view_{text}"} {:id "view_{button}"} {:id "navigate_to_{page}"}],
    :type "feature",
    :id "404-error"},
   "reservation"
   {:description "Present a calendar view to schedule a date and time of a service",
    :complexity 1,
    :capabilities [{:id "book_{products}"} {:id "book_{users}"} {:id "navigate_to_{page}"}],
    :type "feature",
    :id "reservation"},
   "catalog-row"
   {:description
    "Horizontaly browsable row of entitiy cards (actors or commodities) or tags of a one single category or a tag. It prompts only a few fetured entities with a see all button for more",
    :complexity 1,
    :capabilities
    [{:id "view_{products}"}
     {:id "view_{businesses}"}
     {:id "view_{users}"}
     {:id "view_{groups}"}
     {:id "view_{business}_{products}"}
     {:id "view_{user}_{products}"}
     {:id "view_{tags}"}],
    :type "feature",
    :id "catalog-row"},
   "cart"
   {:description "The container that holds items the user plans to purchase.",
    :complexity 1,
    :capabilities
    [{:id "add_{products}_to_cart"} {:id "view_{products}"} {:id "order_{products}"} {:id "navigate_to_{page}"}],
    :type "feature",
    :id "cart"},
   "chat"
   {:description "The real-time messaging window for an active one-to-one or group conversation.",
    :complexity 2,
    :capabilities
    [{:id "chat_{user}"}
     {:id "contact_{user}"}
     {:id "view_{user}"}
     {:id "view_{users}"}
     {:id "follow_{user}"}
     {:id "request_friendships"}],
    :type "feature",
    :id "chat"},
   "form"
   {:description "A configurable input component used to create or edit records.",
    :complexity 1,
    :capabilities
    [{:id "create_{product}"}
     {:id "create_{business}"}
     {:id "create_{post}"}
     {:id "create_{group}"}
     {:id "create_auctions"}
     {:id "create_bids"}
     {:id "create_{tags}"}
     {:id "register_{user}"}
     {:id "login_{user}"}
     {:id "forgot_password_{user}"}
     {:id "change_password_{user}"}
     {:id "onboard_{user}"}
     {:id "subscribe_app"}
     {:id "contact_app"}
     {:id "edit_my_profile"}
     {:id "edit_{user}"}
     {:id "edit_{product}"}
     {:id "edit_{business}"}
     {:id "edit_{post}"}
     {:id "edit_{group}"}],
    :type "feature",
    :id "form"},
   "orders-feed"
   {:description "A collection of orders",
    :complexity 2,
    :capabilities [{:id "manage_orders"} {:id "view_order_user"} {:id "navigate_to_{page}"} {:id "view_orders"}],
    :type "feature",
    :id "orders-feed"},
   "map-feed"
   {:description
    "A collection of entity cards dedicated for user exploration, supports complementry capabilities for that purpose, such as: filters, search, pagination, swipe, scroll, along side a map presenting the same collection of entities by their geo location, sharing the same data and actions with two-way data binding",
    :complexity 1,
    :capabilities
    [{:id "view_{posts}"}
     {:id "view_{user}_{posts}"}
     {:id "view_{group}_{posts}"}
     {:id "create_{posts}"}
     {:id "comment_{posts}"}
     {:id "rate_{post}"}
     {:id "share_{post}"}
     {:id "view_{products}"}
     {:id "view_{user}_{products}"}
     {:id "view_{business}_{products}"}
     {:id "view_{group}_{products}"}
     {:id "add_{products}_to_cart"}
     {:id "search_{products}"}
     {:id "search_{posts}"}
     {:id "search_{users}"}
     {:id "filter_by_tags_{products}"}
     {:id "filter_by_tags_{posts}"}
     {:id "view_{users}"}
     {:id "follow_{user}"}
     {:id "request_friendships"}
     {:id "rate_{users}"}
     {:id "view_{businesses}"}
     {:id "view_{user}_{businesses}"}
     {:id "manage_{users}"}
     {:id "manage_{products}"}
     {:id "manage_{businesses}"}
     {:id "manage_{posts}"}
     {:id "navigate_to_{page}"}],
    :type "feature",
    :id "map-feed"},
   "app-menu-links"
   {:description
    "A list of useful links to key screens in the app such as: general pages, managment screens, and admin tools.",
    :complexity 1,
    :capabilities
    [{:id "edit_my_profile"}
     {:id "change_password_{user}"}
     {:id "view_my_{businesses}"}
     {:id "manage_{users}"}
     {:id "manage_{posts}"}
     {:id "manage_{products}"}
     {:id "manage_{businesses}"}
     {:id "manage_{groups}"}
     {:id "manage_{tags}"}
     {:id "subscribe_app"}
     {:id "contact_app"}],
    :type "feature",
    :id "app-menu-links"},
   "section"
   {:description
    "A reusable full-width content block that includes information about the app, its features or promotions, can be located anywhere in the app",
    :complexity 1,
    :capabilities
    [{:id "view_{segment}"} {:id "view_{image}"} {:id "view_{text}"} {:id "view_{button}"} {:id "navigate_to_{page}"}],
    :type "feature",
    :id "section"},
   "static-info"
   {:description "A static bundle of texts, title, button or images represent a static hard-coded info",
    :complexity 1,
    :capabilities
    [{:id "view_{segment}"} {:id "view_{image}"} {:id "view_{text}"} {:id "view_{button}"} {:id "navigate_to_{page}"}],
    :type "feature",
    :id "static-info"},
   "messages"
   {:description "An inbox that lists all past and ongoing conversations.",
    :complexity 2,
    :capabilities
    [{:id "contact_{user}"} {:id "chat_{user}"} {:id "view_{user}"} {:id "view_{users}"} {:id "navigate_to_{page}"}],
    :type "feature",
    :id "messages"},
   "entity-viewer"
   {:description
    "Present a single entity (actor or commodity) attributes with contextual actions (i.e call to action buttons).",
    :complexity 1,
    :capabilities
    [{:id "view_{user}"}
     {:id "view_{group}"}
     {:id "view_{business}"}
     {:id "view_{posts}"}
     {:id "view_{product}"}
     {:id "rate_{users}"}
     {:id "rate_{businesses}"}
     {:id "rate_{product}"}
     {:id "rate_{post}"}
     {:id "add_{products}_to_cart"}
     {:id "order_{products}"}
     {:id "book_{products}"}
     {:id "book_{users}"}
     {:id "contact_{user}"}
     {:id "chat_{user}"}
     {:id "follow_{user}"}
     {:id "create_bids"}
     {:id "view_bids"}
     {:id "create_auctions"}
     {:id "view_auctions"}
     {:id "create_{tags}"}
     {:id "view_{tags}"}],
    :type "feature",
    :id "entity-viewer"},
   "feed"
   {:description
    "A collection of entity cards dedicated for user exploration, supports complementry capabilities for that purpose, such as: filters, search, pagination, swipe, scroll. Feed can also be nerrowd down to show commodities owned by a spesific actor",
    :complexity 1,
    :capabilities
    [{:id "view_{posts}"}
     {:id "view_{user}_{posts}"}
     {:id "view_{group}_{posts}"}
     {:id "create_{posts}"}
     {:id "comment_{posts}"}
     {:id "rate_{post}"}
     {:id "share_{post}"}
     {:id "view_{products}"}
     {:id "view_{user}_{products}"}
     {:id "view_{business}_{products}"}
     {:id "view_{group}_{products}"}
     {:id "add_{products}_to_cart"}
     {:id "search_{products}"}
     {:id "search_{posts}"}
     {:id "search_{users}"}
     {:id "filter_by_tags_{products}"}
     {:id "filter_by_tags_{posts}"}
     {:id "view_{users}"}
     {:id "follow_{user}"}
     {:id "request_friendships"}
     {:id "rate_{users}"}
     {:id "view_{businesses}"}
     {:id "view_{user}_{businesses}"}
     {:id "manage_{users}"}
     {:id "manage_{products}"}
     {:id "manage_{businesses}"}
     {:id "manage_{posts}"}
     {:id "navigate_to_{page}"}
     {:id "view_orders"}],
    :type "feature",
    :id "feed"},
   "catalog"
   {:description
    "Collection of Horizontaly browsable rows of entity cards (actors or commodities) grouped by a category or a tag. It prompts only a few fetured entities per row with a see all button for more",
    :complexity 2,
    :capabilities
    [{:id "view_{products}"}
     {:id "view_{businesses}"}
     {:id "view_{users}"}
     {:id "view_{groups}"}
     {:id "view_{business}_{products}"}
     {:id "view_{user}_{products}"}
     {:id "view_{tags}"}],
    :type "feature",
    :id "catalog"},
   "checkout"
   {:description "Present the user cart or order detailes alongside a submit button to complete the order.",
    :complexity 1,
    :capabilities
    [{:id "order_{products}"}
     {:id "subscribe_app"}
     {:id "navigate_to_{page}"}
     {:id "book_{products}"}
     {:id "book_{users}"}],
    :type "feature",
    :id "checkout"}
   #_:alert
   #_{:description
      "A transient notification that informs the user of status changes or confirmations."
      :capabilities
      ["view_{text}"
       "view_{button}"]}}

2025-06-27T05:40:13.785249Z

p.s as you can see, in the data sample I sent I already did the transformation (using recursive-path), but you can still change the lookup to look for string values and change the transformation function to keyword

nathanmarz 2025-06-29T16:58:17.032089Z

it's specifically how ALL works, which is what walker is using

nathanmarz 2025-06-29T16:58:42.784169Z

in this case, you can just check that you're in a vector whose first element is :id

2025-06-29T17:02:55.774919Z

My current nav uses map? and then ALL and checks the predicate so I'm sure I'm in a map entry. If I just check for vector? with first element = :id in a walk I might catch an actual vector that its first element is :id. It's probably unlikely in my scenario but it puts a constraint on the nav so I prefer using recursive + map? + ALL

nathanmarz 2025-06-29T02:37:40.173559Z

oh, there is a difference with map entries

nathanmarz 2025-06-29T02:37:50.351069Z

in a transform it navigates to a vector of key/value, not a map entry

2025-06-29T04:57:34.405119Z

Ohh, ok, good to know. This applies for all transforms correct? Can you think of a good predicate to know that I'm in a map entry in transformations?