Используя своп! на вложенной карте - PullRequest
2 голосов
/ 10 февраля 2020

Я следую Главе 6 книги Professional Clojure.

Состояние приложения в настоящее время определяется следующим образом:

(defonce app-state                                          
  (reagent/atom
    {:projects
     {"aaa"
      {:title "Build Whip"
       :stories
       {1 {:title "Design a data model for projects and stories"    
           :status "done"                                           
           :order 1}                                                
        2 {:title "Create a story title entry form"                 
           :order 2}    
        3 {:title "Implement a way to finish stories"
           :order 3}}}}}))                                            

Мне нужно использовать swap!, чтобы добавить новый ключ значение для представления новой истории, идентифицируемое идентификатором со значением указанных полей.

(defn add-story! [app-state project-id title status]     ;
  ; Q. How to use swap! to add a key value pair into :stories?
  (swap! app-state update [:projects project-id :stories] assoc      <- INCORRECT CODE HERE
         (unique) {:title title
                   :status status
                   :order (inc (max-order app-state project-id))}))

Уникальная функция, не показанная здесь, просто генерирует любой уникальный uuid. Функция максимального порядка получает, ну, максимальный порядок. Мне пришлось изменить его, так как глава книги идет вразрез с фактическим поставленным окончательным кодом. Вот моя версия максимального порядка:

(defn max-order [app-state project-id]
  (apply max 0 (map :order (vals (get-in @app-state [:projects project-id :stories])))))

Вопрос : Как я могу использовать swap!, чтобы добавить новое значение ключа в :stories?

I у него было go, но пока оно меня побивало.

У меня такое ощущение, что эта вложенная карта - не лучшее представление - в окончательном коде, представленном для загрузки, автор изменился на модель более реляционного типа с проектами и историями как объектами верхнего уровня, с истории, содержащие project_id, но было бы неплохо решить это первое использование swap!, прежде чем перейти к этому.

Ответы [ 2 ]

4 голосов
/ 10 февраля 2020

Я думаю, что вы можете просто использовать assoc-in в этом случае, который немного проще, чем update-in и лучше описывает, чего вы пытаетесь достичь:

(def app-state
  (atom 
   {:projects
    {"aaa"
     {:title   "Build Whip"
      :stories {1 {:title  "Design a data model for projects and stories"
                   :status "done"
                   :order  1}
                2 {:title "Create a story title entry form"
                   :order 2}
                3 {:title "Implement a way to finish stories"
                   :order 3}}}}}))

(defn unique [] (rand-int 1000000000))

(let [unique-key (unique)]
  (swap! app-state
         assoc-in
         [:projects "aaa" :stories unique-key]
         {:title  (str "Agent " unique-key)
          :status "foxy"
          :order  "some-order-stuff"}))
@app-state
;; => {:projects
;;     {"aaa"
;;      {:title "Build Whip",
;;       :stories
;;       {1 {:title "Design a data model for projects and stories", :status "done", :order 1},
;;        2 {:title "Create a story title entry form", :order 2},
;;        3 {:title "Implement a way to finish stories", :order 3},
;;        295226401 {:title "Agent 295226401", :status "foxy", :order "some-order-stuff"}}}}}

1 голос
/ 10 февраля 2020

Обязательно изучайте Clojure CheatSheet ежедневно! Также, больше ссылок на документацию здесь .

Я думаю, вам просто нужно использовать update-in

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [clojure.string :as str]
    ))

(def state
  {:projects
   {"aaa"
    {:title   "Build Whip"
     :stories {1 {:title  "Design a data model for projects and stories"
                  :status "done"
                  :order  1}
               2 {:title "Create a story title entry form"
                  :order 2}
               3 {:title "Implement a way to finish stories"
                  :order 3}}}}})

(dotest
  (let [result (update-in state [:projects "aaa" :stories]
                 assoc 99 {:title  "Agent 99"
                           :status "foxy"
                           :order  "some-order-stuff"})
        ]
    (spyx-pretty result)))

с результатом:

-------------------------------
   Clojure 1.10.1    Java 13
-------------------------------

Testing tst.demo.core
result => 
{:projects
 {"aaa"
  {:title "Build Whip",
   :stories
   {1
    {:title "Design a data model for projects and stories",
     :status "done",
     :order 1},
    2   {:title "Create a story title entry form", :order 2},
    3   {:title "Implement a way to finish stories", :order 3},
    99  {:title "Agent 99", :status "foxy", :order "some-order-stuff"}}}}}

Когда данные находятся внутри атома, решение почти такое же:

  (def state-atom (atom state))
  (let [result (swap! state-atom update-in [:projects "aaa" :stories]
                 assoc 99 {:title  "Agent 99"
                           :status "foxy"
                           :order  "some-order-stuff"})]
    (spyx-pretty @state-atom))

с результатом

(clojure.core/deref state-atom) => 
{:projects
 {"aaa"
  {:title "Build Whip",
   :stories
   {1
    {:title "Design a data model for projects and stories",
     :status "done",
     :order 1},
    2 {:title "Create a story title entry form", :order 2},
    3 {:title "Implement a way to finish stories", :order 3},
    99
    {:title "Agent 99", :status "foxy", :order "some-order-stuff"}}}}}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...