java.lang.String cannot be cast to clojure.lang.IFn - clojure

I am just trying to set the value of the cookie named test.
example: http://www.luminusweb.net/docs/sessions_cookies.md
My code:
(GET "/new-location" req (new-location req)
(-> "cookie set" response (update-in [:cookies "test" :value] "Alice")))
Error:
java.lang.String cannot be cast to clojure.lang.IFn
In case you want to see new-location function:
(defn new-location [resp]
(render "{{resp}}" {:resp resp}))
Resp Output:
{:ssl-client-cert nil, :cookies {"test" {:value "5Zn5Z6shY7vJFH3dYyhmDzfB/MmLkZVShKxxrNXop7QgubGBLBGQMzsdPX3c9kJkc/H3oFns/Y3+yU5RNXmBCp+Hs5ha0mEMhiRcGw04Z4w=--BZCShBKs13BCNkVGtyKAtuOMqPCH+sFl3t39qnM6Eks="}}, :remote-addr "127.0.0.1", :params {}, :flash nil, :handler-type :undertow, :route-params {}, :headers {"host" "localhost:3000", "user-agent" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.15 Safari/537.36", "cookie" "test=5Zn5Z6shY7vJFH3dYyhmDzfB%2FMmLkZVShKxxrNXop7QgubGBLBGQMzsdPX3c9kJkc%2FH3oFns%2FY3%2ByU5RNXmBCp%2BHs5ha0mEMhiRcGw04Z4w%3D--BZCShBKs13BCNkVGtyKAtuOMqPCH%2BsFl3t39qnM6Eks%3D", "connection" "keep-alive", "upgrade-insecure-requests" "1", "accept" "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "accept-language" "en-GB,en-US;q=0.8,en;q=0.6", "accept-encoding" "gzip, deflate, sdch", "dnt" "1", "cache-control" "max-age=0"}, :server-port 3000, :content-length -1, :form-params {}, :compojure/route [:get "/new-location"], :session/key "5Zn5Z6shY7vJFH3dYyhmDzfB/MmLkZVShKxxrNXop7QgubGBLBGQMzsdPX3c9kJkc/H3oFns/Y3+yU5RNXmBCp+Hs5ha0mEMhiRcGw04Z4w=--BZCShBKs13BCNkVGtyKAtuOMqPCH+sFl3t39qnM6Eks=", :server-exchange #object[io.undertow.server.HttpServerExchange 0x2035c15c "HttpServerExchange{ GET /new-location}"], :query-params {}, :content-type nil, :path-info "/new-location", :character-encoding nil, :context "", :uri "/new-location", :server-name "localhost", :query-string "", :body #object[io.undertow.io.UndertowInputStream 0x2ef23b2a "io.undertow.io.UndertowInputStream#2ef23b2a"], :multipart-params {}, :scheme :http, :request-method :get, :session {:ring.middleware.session-timeout/idle-timeout 1438182648}}

update-in takes a function for providing the new value, not the new value - do
(update-in [:cookies "test" :value] (constantly "Alice"))
instead.

Related

Swap nested specific item in map finding by key value, the position in map

We have a cursor or atom map with this example data:
#<Cursor: [:customer] {:name Diego Peña,
:addresses [{:id 23, :province Madrid, :country 1, :descripcion aaeeeeeeee iii oooo4444, :locality Gali gali, :country_name SPAIN, :direccion Street Cierva, :id 3, :postalcode 30203, :principal true, :customer 17}
{:id 35, :province Madrid, :country nil, :descripcion yyy lalala3, :locality Lalala, :direccion calle Maria 3 , :postalcode 333, :principal false, :customer 17}
{:id 6, :province Madrid, :country 2, :descripcion otra direccioncita444, :locality Leleele, :country_name SPAIN, :direccion Direccion calle Ooo, :postalcode 1236, :main false, :customer 17}
{:id 27, :province Madrid, :country 1, :descripcion grandisima, :locality Alcantarilla, :country_name SPAIN, :direccion C/ 3 Mayo, :postalcode 3001, :main false, :customer 17}
]}>
I need to change the values of a searched address by id. I have managed to locate the address by the value of the id:
(defn get-address [pk]
(->> #db/customer :addresses (filter #(= (int pk) (int (:id %)))) first)
)
I can change all addresses with this: :ok #(swap! db/customer assoc-in [:addresses] %)}). I need to change data for a specific address from the API response.
I am close to getting it, but with this approach I am missing the position or the index of the item: #(swap! db/client assoc-in [:addresses ¿position or index in map?] %) we have the id of item address.
Perhaps this approach is wrong, a better one?
The assoc, assoc-in, update, and update-in functions work also on vectors. In Clojure, vectors are associative data structures where the key is the numeric index (O..n) and the value is the item at position n.
So you can do:
(assoc [:a :b :c] 1 :new-value)
;; ^ ^ ^
;; 0 1 2
;; => [:a :new-value :c]
Based on your example, you will need this:
(defn address-index-by-id
"Take an `address` map and look it up by `:id` in the `addresses` list.
Return the numeric index where it was found, nil if not found."
[address addresses]
(->> (map-indexed vector addresses)
;; produce a seq `([0 val-at-index-0] … [n val-at-index-n])`
(filter (fn [[_index {:keys [id]}]] (= id (:id address)))) ;; filter by id
(ffirst) ;; get the index of the first match
))
(defn set-address
"Take a `customer` map and an `address` map. Will put the `address` in the
customer's addresses list. If an address with the same :id key is already
present in this list, it will be overwritten."
[customer address]
(if-let [existing-index (address-index-by-id address (:addresses customer))]
(assoc-in customer [:addresses existing-index] address)
(update customer :addresses conj address)))
Usage:
(set-address {:name "Diego Peña"
:addresses []}
{:id 1
:province "Madrid"})
;; => {:name "Diego Peña", :addresses [{:id 1, :province "Madrid"}]}
(-> {:name "Diego Peña"
:addresses [{:id 1
:province "Madrid"
:main true}
{:id 2
:province "Barcelona"
:main false}]}
(set-address {:id 2
:province "Barcelona"
:main true})
(set-address {:id 1
:province "Madrid"
:main false}))
;; => {:name "Diego Peña", :addresses [{:id 1, :province "Madrid", :main false} {:id 2, :province "Barcelona", :main true}]}
;; And of course if your `customer` is stored in an Atom:
(swap! customer set-address {:id 1, :province "Madrid", :main true})
It looks like you are pulling data out of a database of some kind. If so, you should let the DB search for the ID in question. You can then read or update that record.
If you really need to do it in Clojure, you can search for the desired customer map using the tupelo.forest library. Here is an example with your data:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require [tupelo.forest :as tf]))
(def customer
{:name "Diego Peña",
:addresses
[{:id 23
:province "Madrid"
:country 1
:descripcion " aaeeeeeeee iii oooo4444"
:locality "Gali gali"
:country_name "SPAIN"
:direccion "Street Cierva"
:postalcode 30203
:principal true
}
{:id 35
:province "Madrid"
:country nil
:descripcion "yyy lalala3"
:locality "Lalala"
:direccion "calle Maria 3"
:postalcode 333
:principal false
:customer 17}
{:id 6
:province "Madrid"
:country 2
:descripcion "otra direccioncita444"
:locality "Leleele"
:country_name "SPAIN"
:direccion "Direccion calle Ooo"
:postalcode 1236
:main false
:customer 17}
{:id 27
:province "Madrid"
:country 1
:descripcion "grandisima"
:locality "Alcantarilla"
:country_name "SPAIN"
:direccion "C / 3 Mayo"
:postalcode 3001
:main false
:customer 17}
]})
and some code to find customer with :id 35
(dotest
(tf/with-forest (tf/new-forest)
(let [root-hid (tf/add-tree-edn customer)
cust-num 35
paths-found (tf/find-paths root-hid [:**
{:tag :tupelo.forest/entry, :tupelo.forest/key :id}
{:tupelo.forest/value cust-num}])
cust-path-rev (reverse (last paths-found))
cust-entity-hid (xthird cust-path-rev)
]
(is= (tf/hid->bush (xfirst cust-path-rev)) [#:tupelo.forest{:value 35, :index nil}])
(is= (tf/hid->tree cust-entity-hid)
{:tag :tupelo.forest/entity,
:tupelo.forest/index 1,
:tupelo.forest/kids [{:tag :tupelo.forest/entry,
:tupelo.forest/key :locality,
:tupelo.forest/kids [#:tupelo.forest{:kids [], :value "Lalala", :index nil}]}
{:tag :tupelo.forest/entry,
:tupelo.forest/key :customer,
:tupelo.forest/kids [#:tupelo.forest{:kids [], :value 17, :index nil}]}
{:tag :tupelo.forest/entry,
:tupelo.forest/key :descripcion,
:tupelo.forest/kids [#:tupelo.forest{:kids [], :value "yyy lalala3", :index nil}]}
{:tag :tupelo.forest/entry,
:tupelo.forest/key :direccion,
:tupelo.forest/kids [#:tupelo.forest{:kids [], :value "calle Maria 3", :index nil}]}
{:tag :tupelo.forest/entry,
:tupelo.forest/key :id,
:tupelo.forest/kids [#:tupelo.forest{:kids [], :value 35, :index nil}]}
{:tag :tupelo.forest/entry,
:tupelo.forest/key :postalcode,
:tupelo.forest/kids [#:tupelo.forest{:kids [], :value 333, :index nil}]}
{:tag :tupelo.forest/entry,
:tupelo.forest/key :principal,
:tupelo.forest/kids [#:tupelo.forest{:kids [], :value false, :index nil}]}
{:tag :tupelo.forest/entry,
:tupelo.forest/key :province,
:tupelo.forest/kids [#:tupelo.forest{:kids [], :value "Madrid", :index nil}]}
{:tag :tupelo.forest/entry,
:tupelo.forest/key :country,
:tupelo.forest/kids [#:tupelo.forest{:kids [], :value nil, :index nil}]}]})
You can convert the data from the internal tree format back into EDN data:
(is= (tf/hid->edn cust-entity-hid)
{:locality "Lalala",
:customer 17,
:descripcion "yyy lalala3",
:direccion "calle Maria 3",
:id 35,
:postalcode 333,
:principal false,
:province "Madrid",
:country nil})
)))
You never really said how you want to change the data. Again, it would probably be best to do this with the DB rather than changing Clojure data structures.
Update
Another option is to use a recursive walk like clojure.walk/postwalk or the enhanced tupelo.core/walk-with-parents. Example to uppercase street name for cust ID #35
(dotest
(let [modified (t/walk-with-parents customer
{:enter (fn [parents item]
(with-nil-default item
(when (and (map? item)
(t/submap? {:id 35} item))
(spyx-pretty item)
(update item :direccion str/upper-case))))})]
(is= modified {:name "Diego Peña",
:addresses
[{:locality "Gali gali",
:descripcion " aaeeeeeeee iii oooo4444",
:country_name "SPAIN",
:direccion "Street Cierva",
:id 23,
:postalcode 30203,
:principal true,
:province "Madrid",
:country 1}
{:locality "Lalala",
:customer 17,
:descripcion "yyy lalala3",
:direccion "CALLE MARIA 3",
:id 35,
:postalcode 333,
:principal false,
:province "Madrid",
:country nil}
{:locality "Leleele",
:customer 17,
:descripcion "otra direccioncita444",
:country_name "SPAIN",
:direccion "Direccion calle Ooo",
:id 6,
:postalcode 1236,
:main false,
:province "Madrid",
:country 2}
{:locality "Alcantarilla",
:customer 17,
:descripcion "grandisima",
:country_name "SPAIN",
:direccion "C / 3 Mayo",
:id 27,
:postalcode 3001,
:main false,
:province "Madrid",
:country 1}]})))

getting a flat record data structure out of a nested list in Clojure

Let's say I have a list of lists representing a tree-structure in clojure, like
'(a (b (c d)) (e (f)))
and I want to translate it into a record format like this (for the purpose of passing it off to a visualization package):
[{:id "0" :label "a" :parent nil}
{:id "1" :label "b" :parent "0"}
{:id "2" :label "c" :parent "1"}
{:id "3" :label "d" :parent "1"}
{:id "4" :label "e" :parent "0"}
{:id "5" :label "f" :parent "4"}]
What's the right way to go about this?
I am pretty shaky with this, but I would think of starting with defrecord, and then some way of looping through the tree, but I don't know how to get started.
(def tree '(a (b (c d)) (e (f))))
(defn list-to-record [l]
(defrecord rec [id name parent])
(let [counter (atom 0)]
(into [] (map ->rec
... ... ...))))
(list-to-record tree)
Perhaps I should be using clojure.walk?
Edit: to clarify, this should work regardless of what the labels are, so changing the labels in the input list shouldn't do anything to the resulting structure (the :parent values for each :id). That is, the following list, just as above, but with the labels all the same as each other
'(a (a (a a)) (a (a)))
should get translated into
[{:id "0" :label "a" :parent nil}
{:id "1" :label "a" :parent "0"}
{:id "2" :label "a" :parent "1"}
{:id "3" :label "a" :parent "1"}
{:id "4" :label "a" :parent "0"}
{:id "5" :label "a" :parent "4"}]
Here's a way to do it with Clojure zippers and loop + recur:
(defn index-zipper [z]
(loop [loc z, next-id 0, parent-ids [], acc []]
(cond
(z/end? loc) acc
(and (z/node loc) (not (z/branch? loc)))
(recur
(z/next loc)
(inc next-id)
(cond
(some-> (z/right loc) z/branch?) (conj parent-ids next-id)
(not (z/right loc)) (some-> parent-ids not-empty pop)
:else parent-ids)
(conj acc
{:id (str next-id)
:label (str (z/node loc))
:parent (when (seq parent-ids)
(str (peek parent-ids)))}))
:else
(recur (z/next loc) next-id parent-ids acc))))
The loop has bindings for:
Current zipper location
Next :id value, to be incremented each time we see a leaf node
Stack (vector) of current parent :ids, which will be pushed/popped as the zipper descends/ascends. The :parent value for each leaf node is on the top of the parent-ids stack.
accumulator vector for leaf node maps
You can call the function with a zipper:
(index-zipper (z/seq-zip '(a (b (c d)) (e (f)))))
=>
[{:id "0", :label "a", :parent nil}
{:id "1", :label "b", :parent "0"}
{:id "2", :label "c", :parent "1"}
{:id "3", :label "d", :parent "1"}
{:id "4", :label "e", :parent "0"}
{:id "5", :label "f", :parent "4"}]
(index-zipper (z/seq-zip '(a (a (a a)) (a (a)))))
=>
[{:id "0", :label "a", :parent nil}
{:id "1", :label "a", :parent "0"}
{:id "2", :label "a", :parent "1"}
{:id "3", :label "a", :parent "1"}
{:id "4", :label "a", :parent "0"}
{:id "5", :label "a", :parent "4"}]
Here is one way to do it. You just have to add the part about assigning an "id" to each node.
Note also that you should re-format your input data so each node is in Hiccup format (i.e. even singleton nodes w/o children are wrapped in a vector).
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require [tupelo.core :as t]))
(def tree
[:a
[:b
[:c]
[:d]]
[:e
[:f]]])
(def relationships (atom []))
(defn save-relationships
[parent-id curr-node]
(let [curr-id (first curr-node)
kid-nodes (rest curr-node)]
(swap! relationships #(conj % {:parent parent-id :label curr-id}))
(doseq [kid-node kid-nodes]
(save-relationships curr-id kid-node))))
(dotest
(reset! relationships [])
(save-relationships nil tree)
(spyx-pretty #relationships))
with result:
~/expr/demo >
~/expr/demo > lein test
lein test _bootstrap
-------------------------------
Clojure 1.10.1 Java 12
-------------------------------
lein test tst.demo.core
(clojure.core/deref relationships) =>
[{:parent nil, :label :a}
{:parent :a, :label :b}
{:parent :b, :label :c}
{:parent :b, :label :d}
{:parent :a, :label :e}
{:parent :e, :label :f}]
Ran 2 tests containing 0 assertions.
0 failures, 0 errors.

Clojure: Transform nested maps into custom map keeping only specific attributes

I have a vector of maps (result of xml/parse) which contains the following vector of nested maps (I already got rid of some parts I don't want to keep):
[
{:tag :SoapObject, :attrs nil, :content [
{:tag :ObjectData, :attrs nil, :content [
{:tag :FieldName, :attrs nil, :content ["ID"]}
{:tag :FieldValue, :attrs nil, :content ["8d8edbb6-cb0f-11e8-a8d5-f2801f1b9fd1"]}
]}
{:tag :ObjectData, :attrs nil, :content [
{:tag :FieldName, :attrs nil, :content ["Attribute_1"]}
{:tag :FieldValue, :attrs nil, :content ["Value_1a"]}
]}
{:tag :ObjectData, :attrs nil, :content [
{:tag :FieldName, :attrs nil, :content ["Attribute_2"]}
{:tag :FieldValue, :attrs nil, :content ["Value_2a"]}
]}
]}
{:tag :SoapObject, :attrs nil, :content [
{:tag :ObjectData, :attrs nil, :content [
{:tag :FieldName, :attrs nil, :content ["ID"]}
{:tag :FieldValue, :attrs nil, :content ["90e39036-cb0f-11e8-a8d5-f2801f1b9fd1"]}
]}
{:tag :ObjectData, :attrs nil, :content [
{:tag :FieldName, :attrs nil, :content ["Attribute_1"]}
{:tag :FieldValue, :attrs nil, :content ["Value_1b"]}
]}
{:tag :ObjectData, :attrs nil, :content [
{:tag :FieldName, :attrs nil, :content ["Attribute_2"]}
{:tag :FieldValue, :attrs nil, :content ["Value_2b"]}
]}
]}
]
Now I want to extract only some specific data from this structure, producing a result which looks like this:
[
{"ID" "8d8edbb6-cb0f-11e8-a8d5-f2801f1b9fd1",
"Attribute_1" "Value_1a",
"Attribute_2" "Value_1a"}
{"ID" "90e39036-cb0f-11e8-a8d5-f2801f1b9fd1",
"Attribute_1" "Value_1b",
"Attribute_2" "Value_1b"}
]
Which clojure tool could help me accomplish this?
I've found another question which is a bit similar, but whenever I tried some version of a map call the result I got was some kind of clojure.lang.LazySeq or clojure.core$map which I couldn't get to print properly to verify the result.
usually you can start from the bottom, gradually going up:
first you would like to parse the attr item:
(def first-content (comp first :content))
(defn get-attr [{[k v] :content}]
[(first-content k)
(first-content v)])
user> (get-attr {:tag :ObjectData, :attrs nil, :content [
{:tag :FieldName, :attrs nil, :content ["ID"]}
{:tag :FieldValue, :attrs nil, :content ["90e39036-cb0f-11e8-a8d5-f2801f1b9fd1"]}
]})
;;=> ["ID" "90e39036-cb0f-11e8-a8d5-f2801f1b9fd1"]
then you would turn every item into a map of attrs:
(defn parse-item [item]
(into {} (map get-attr (:content item))))
(parse-item {:tag :SoapObject, :attrs nil, :content [
{:tag :ObjectData, :attrs nil, :content [
{:tag :FieldName, :attrs nil, :content ["ID"]}
{:tag :FieldValue, :attrs nil, :content ["90e39036-cb0f-11e8-a8d5-f2801f1b9fd1"]}
]}
{:tag :ObjectData, :attrs nil, :content [
{:tag :FieldName, :attrs nil, :content ["Attribute_1"]}
{:tag :FieldValue, :attrs nil, :content ["Value_1b"]}
]}
{:tag :ObjectData, :attrs nil, :content [
{:tag :FieldName, :attrs nil, :content ["Attribute_2"]}
{:tag :FieldValue, :attrs nil, :content ["Value_2b"]}
]}
]})
;;=> {"ID" "90e39036-cb0f-11e8-a8d5-f2801f1b9fd1", "Attribute_1" "Value_1b", "Attribute_2" "Value_2b"}
so the last thing you need do, is to map over the top level form, producing the required result:
(mapv parse-item data)
;;=> [{"ID" "8d8edbb6-cb0f-11e8-a8d5-f2801f1b9fd1", "Attribute_1" "Value_1a", "Attribute_2" "Value_2a"}
;; {"ID" "90e39036-cb0f-11e8-a8d5-f2801f1b9fd1", "Attribute_1" "Value_1b", "Attribute_2" "Value_2b"}]
You can easily solve tree-based problems using the Tupelo Forest library. You can see a video introduction from last year's Clojure Conj here.
For your problem, I'd approach it as follows. First, the data:
(dotest
(let [data-enlive
{:tag :root
:attrs nil
:content
[{:tag :SoapObject, :attrs nil,
:content
[{:tag :ObjectData, :attrs nil,
:content [{:tag :FieldName, :attrs nil, :content ["ID"]}
{:tag :FieldValue, :attrs nil, :content ["8d8edbb6-cb0f-11e8-a8d5-f2801f1b9fd1"]}]}
{:tag :ObjectData, :attrs nil,
:content [{:tag :FieldName, :attrs nil, :content ["Attribute_1"]}
{:tag :FieldValue, :attrs nil, :content ["Value_1a"]}]}
{:tag :ObjectData, :attrs nil,
:content [{:tag :FieldName, :attrs nil, :content ["Attribute_2"]}
{:tag :FieldValue, :attrs nil, :content ["Value_2a"]}]}]}
{:tag :SoapObject, :attrs nil,
:content
[{:tag :ObjectData, :attrs nil,
:content [{:tag :FieldName, :attrs nil, :content ["ID"]}
{:tag :FieldValue, :attrs nil, :content ["90e39036-cb0f-11e8-a8d5-f2801f1b9fd1"]}]}
{:tag :ObjectData, :attrs nil,
:content [{:tag :FieldName, :attrs nil, :content ["Attribute_1"]}
{:tag :FieldValue, :attrs nil, :content ["Value_1b"]}]}
{:tag :ObjectData, :attrs nil,
:content [{:tag :FieldName, :attrs nil, :content ["Attribute_2"]}
{:tag :FieldValue, :attrs nil, :content ["Value_2b"]}]}]}]}]
and then the code
(with-debug-hid
(with-forest (new-forest)
(let [root-hid (add-tree-enlive data-enlive)
soapobj-hids (find-hids root-hid [:root :SoapObject])
objdata->map (fn [objdata-hid]
(let [fieldname-node (hid->node (find-hid objdata-hid [:ObjectData :FieldName]))
fieldvalue-node (hid->node (find-hid objdata-hid [:ObjectData :FieldValue]))]
{ (grab :value fieldname-node) (grab :value fieldvalue-node) }))
soapobj->map (fn [soapobj-hid]
(apply glue
(for [objdata-hid (hid->kids soapobj-hid)]
(objdata->map objdata-hid))))
results (mapv soapobj->map soapobj-hids)]
with intermediate results:
(is= (hid->bush root-hid)
[{:tag :root}
[{:tag :SoapObject}
[{:tag :ObjectData}
[{:tag :FieldName, :value "ID"}]
[{:tag :FieldValue, :value "8d8edbb6-cb0f-11e8-a8d5-f2801f1b9fd1"}]]
[{:tag :ObjectData}
[{:tag :FieldName, :value "Attribute_1"}]
[{:tag :FieldValue, :value "Value_1a"}]]
[{:tag :ObjectData}
[{:tag :FieldName, :value "Attribute_2"}]
[{:tag :FieldValue, :value "Value_2a"}]]]
[{:tag :SoapObject}
[{:tag :ObjectData}
[{:tag :FieldName, :value "ID"}]
[{:tag :FieldValue, :value "90e39036-cb0f-11e8-a8d5-f2801f1b9fd1"}]]
[{:tag :ObjectData}
[{:tag :FieldName, :value "Attribute_1"}]
[{:tag :FieldValue, :value "Value_1b"}]]
[{:tag :ObjectData}
[{:tag :FieldName, :value "Attribute_2"}]
[{:tag :FieldValue, :value "Value_2b"}]]]])
(is= soapobj-hids [:0009 :0013])
and the final results:
(is= results
[{"ID" "8d8edbb6-cb0f-11e8-a8d5-f2801f1b9fd1",
"Attribute_1" "Value_1a",
"Attribute_2" "Value_2a"}
{"ID" "90e39036-cb0f-11e8-a8d5-f2801f1b9fd1",
"Attribute_1" "Value_1b",
"Attribute_2" "Value_2b"}]))))))
Further documentation is still in progress, but you can see API docs here and a live example of your problem here.
You can also compose transducers. I was reading the other day something on JUXT blog about creating xpath like functionality with transducers.
(def children (map :content))
(defn tagp [pred]
(filter (comp pred :tag)))
(defn tag= [tag-name]
(tagp (partial = tag-name)))
(def text (comp (mapcat :content) (filter string?)))
(defn fields [obj-datas]
(sequence (comp
(tag= :ObjectData)
(mapcat :content)
text)
obj-datas))
(defn clean [xml-map]
(let [fields-list (sequence (comp
(tag= :SoapObject)
children
(map fields))
xml-map)]
(map (partial apply hash-map) fields-list)))
No need for fancy tools here. You can get away with the simplest chunk of code.
(use '[plumbing.core])
(let [A ...your-data...]
(map (fn->> :content
(mapcat :content)
(mapcat :content)
(apply hash-map))
A))

how to extract data in nested list/vector clojure

I have parse xml and get the following result
(({:tag :Column,
:attrs {:Name "VENDOR_KEY", :Type "Int", :NotNull "Yes"},
:content nil}
{:tag :Column,
:attrs {:Name "RETAILER_KEY", :Type "Int", :NotNull "Yes"},
:content nil}
{:tag :Column,
:attrs {:Name "ITEM_KEY", :Type "Int", :NotNull "Yes"},
:content nil})
({:tag :Column,
:attrs {:Name "Store_Key", :Type "Int", :NotNull "Yes"},
:content nil}))
then how to convert it to the following, basically I want to extract the value of key :attrs in nested list.
(
({:Name "VENDOR_KEY", :Type "Int", :NotNull "Yes"},
{:Name "RETAILER_KEY", :Type "Int", :NotNull "Yes"},
{:Name "ITEM_KEY", :Type "Int", :NotNull "Yes"}),
({:Name "Store_Key", :Type "Int", :NotNull "Yes"})
)
so yes right here your solution as
hsestupin said
(map #(map :attrs %) result)
i am assuming result is your input data.

merging hash map in clojure giving unexpected results

I am merging two hashmaps in clojure, but it is giving unexpected results. Below are the two datasets I am merging:
({:TEST"E", :EMEA "0", :NA "0", :ASPAC "180"}
{:TEST"B", :EMEA "0", :NA "70", :ASPAC "0"}
{:TEST"D", :EMEA "38", :NA "0", :ASPAC "0"}
{:TEST"C", :EMEA "0", :NA "0", :ASPAC "0"}
{:TEST"G", :EMEA "360", :NA "0", :ASPAC "0"}
{:TEST"A", :EMEA "45", :NA "0", :ASPAC "0"}
{:TEST"F", :EMEA "0", :NA "0", :ASPAC "66"})
({:TEST"A", :EMEA_1 "40", :NA_1 "0", :ASPAC_1 "0"}
{:TEST"B", :EMEA_1 "90", :NA_1 "0", :ASPAC_1 "0"}
{:TEST"H", :EMEA_1 "0", :NA_1 "120", :ASPAC_1 "0"}
{:TEST"C", :EMEA_1 "0", :NA_1 "85", :ASPAC_1 "0"})
I am expecting to see somethig like this:
({:TEST"A", :EMEA "45", :NA "0", :ASPAC "0", :EMEA_1 "40", :NA_1 "0", :ASPAC_1 "0"}
{:TEST"B", :EMEA "0", :NA "70", :ASPAC "0", :EMEA_1 "90", :NA_1 "0", :ASPAC_1 "0"}
{:TEST"C", :EMEA "0", :NA "0", :ASPAC "0", :EMEA_1 "0", :NA_1 "85", :ASPAC_1 "0"}
{:TEST"D", :EMEA "38", :NA "0", :ASPAC "0", :EMEA_1 nil, :NA_1 nil, :ASPAC_1 nil}
{:TEST"E", :EMEA "0", :NA "0", :ASPAC "180", :EMEA_1 nil, :NA_1 nil, :ASPAC_1 nil}
{:TEST"F", :EMEA "0", :NA "0", :ASPAC "66", :EMEA_1 nil, :NA_1 nil, :ASPAC_1 nil}
{:TEST"G", :EMEA "360", :NA "0", :ASPAC "0", :EMEA_1 nil, :NA_1 nil, :ASPAC_1 nil}
{:TEST"H", :EMEA nil, :NA nil, :ASPAC nil, :EMEA_1 "0", :NA_1 "120", :ASPAC_1 "0"})
I have tried using merge, merge-wth, apply merge-with but nothing is giving me the results I expected.
Any adivse on how I can get to my desired resultset or why I am actually not geting what I am expecting would also be helpful.
Thanks you.
Given your two collections are defined as coll1 and coll2:
(map (partial apply merge) (-> (clojure.set/union coll1 coll2)
(clojure.set/index [:TEST])
vals))
should lead to your desired result.
If you don't want clojure.set you may also use
(map (partial apply merge) (->> (concat coll1 coll2)
(group-by :TEST)
vals))
However looking at your data structures, they rather look like a case for clojure.set.
You may note that the result will not have [:key nil] like entries. If you want them, there certainly is a way but relying on them goes against the meaning of nil as nothing.