Поменяйте местами вложенный специфицированный элемент c в поиске карты по значению ключа, позиции на карте - PullRequest
0 голосов
/ 28 марта 2020

У нас есть карта курсора или атома с данными этого примера:

    #<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. Мне удалось найти адрес по значению идентификатора:

(defn get-address [pk]
   (->> @db/customer :addresses (filter #(= (int pk) (int (:id %)))) first)
)

Я могу изменить все адреса с помощью этого: :ok #(swap! db/customer assoc-in [:addresses] %)}). Мне нужно изменить данные для конкретный c адрес из ответа API.

Я близок к получению, но при таком подходе мне не хватает позиции или индекса предмета: #(swap! db/client assoc-in [:addresses ¿position or index in map?] %) у нас есть идентификатор адреса предмета.

Возможно, такой подход неправильный, лучше?

Ответы [ 2 ]

1 голос
/ 28 марта 2020

Функции asso c, asso c -in , update и update-in работают также на векторы. В Clojure векторы - это ассоциативные структуры данных, ключом которых является число c index (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})
0 голосов
/ 28 марта 2020

Похоже, вы извлекаете данные из какой-то базы данных. Если это так, вы должны позволить БД искать соответствующий идентификатор. Затем вы можете прочитать или обновить эту запись.

Если вам действительно нужно сделать это в Clojure, вы можете найти нужную карту клиента, используя библиотеку tupelo.forest . Вот пример с вашими данными:

(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. Пример названия улицы в верхнем регистре для 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}]})))
...