通过键值,地图中的位置交换地图查找中嵌套的特定项目
Swap nested specific item in map finding by key value, the position in map
我们有一个带有此示例数据的游标或原子图:
#<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}
]}>
我需要通过 id 更改搜索地址的值。我已经设法通过 id 的值找到地址:
(defn get-address [pk]
(->> @db/customer :addresses (filter #(= (int pk) (int (:id %)))) first)
)
我可以用这个更改所有地址::ok #(swap! db/customer assoc-in [:addresses] %)})
。我需要从 API 响应中更改 特定地址 的数据。
我快要得到它了,但是通过这种方法我丢失了项目的位置或索引:#(swap! db/client assoc-in [:addresses ¿position or index in map?] %)
我们有项目地址的 ID。
也许这种方法是错误的,有更好的方法吗?
assoc, assoc-in, update, and update-in 函数也适用于向量。在 Clojure 中,向量是关联数据结构,其中键是数字索引 (O..n
),值是位置 n
.
处的项目
所以你可以这样做:
(assoc [:a :b :c] 1 :new-value)
;; ^ ^ ^
;; 0 1 2
;; => [:a :new-value :c]
根据您的示例,您将需要:
(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)))
用法:
(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})
看起来您正在从某种数据库中提取数据。如果是这样,您应该让数据库搜索有问题的 ID。然后您可以读取或更新该记录。
如果您确实需要在Clojure 中进行,您可以使用tupelo.forest library 搜索所需的客户地图。以下是您的数据示例:
(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}
]})
和一些用于查找 :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}]}]})
您可以将数据从内部树格式转换回 EDN 数据:
(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})
)))
您从未真正说过要如何更改数据。同样,最好使用数据库而不是更改 Clojure 数据结构来执行此操作。
更新
另一种选择是使用像 clojure.walk/postwalk
或增强的 tupelo.core/walk-with-parents
这样的递归遍历。客户 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}]})))
我们有一个带有此示例数据的游标或原子图:
#<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}
]}>
我需要通过 id 更改搜索地址的值。我已经设法通过 id 的值找到地址:
(defn get-address [pk]
(->> @db/customer :addresses (filter #(= (int pk) (int (:id %)))) first)
)
我可以用这个更改所有地址::ok #(swap! db/customer assoc-in [:addresses] %)})
。我需要从 API 响应中更改 特定地址 的数据。
我快要得到它了,但是通过这种方法我丢失了项目的位置或索引:#(swap! db/client assoc-in [:addresses ¿position or index in map?] %)
我们有项目地址的 ID。
也许这种方法是错误的,有更好的方法吗?
assoc, assoc-in, update, and update-in 函数也适用于向量。在 Clojure 中,向量是关联数据结构,其中键是数字索引 (O..n
),值是位置 n
.
所以你可以这样做:
(assoc [:a :b :c] 1 :new-value)
;; ^ ^ ^
;; 0 1 2
;; => [:a :new-value :c]
根据您的示例,您将需要:
(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)))
用法:
(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})
看起来您正在从某种数据库中提取数据。如果是这样,您应该让数据库搜索有问题的 ID。然后您可以读取或更新该记录。
如果您确实需要在Clojure 中进行,您可以使用tupelo.forest library 搜索所需的客户地图。以下是您的数据示例:
(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}
]})
和一些用于查找 :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}]}]})
您可以将数据从内部树格式转换回 EDN 数据:
(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})
)))
您从未真正说过要如何更改数据。同样,最好使用数据库而不是更改 Clojure 数据结构来执行此操作。
更新
另一种选择是使用像 clojure.walk/postwalk
或增强的 tupelo.core/walk-with-parents
这样的递归遍历。客户 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}]})))