Как обновить векторный элемент в Clojure?
Дано:
(def my-vec [{:id 0 :a "foo" :b "bar"} {:id 1 :a "baz" :b "spam"}
{:id 2 :a "qux" :b "fred"}])
Как я могу идиоматически обновить * элемент в my-vec с помощью :id=1
, чтобы иметь значения :a="baz2"
и :b="spam2"
?
*: Я понимаю, что на самом деле я бы не обновлял my-vec, но действительно возвращал новый вектор, который идентичен my-vec, за исключением значений замены.
Ответы
Ответ 1
отображает функцию над вектором карт, которая либо создает модифицированное отображение, если ключ соответствует или использует оригинал, если ключи не совпадают, а затем возвращает результат обратно в вектор
(vec (map #(if (= (:id %) 1)
(assoc % :a "baz2" :b "spam2")
%)))
Это можно сделать более лаконично, хотя на самом деле это показывает, где происходит структурное разделение.
Ответ 2
Знаете ли вы заранее, что карта с id == 1 является второй картой в вашем векторе? Если да:
user> (-> my-vec
(assoc-in [1 :a] "baz2")
(assoc-in [1 :b] "spam2"))
[{:id 0, :a "foo", :b "bar"} {:id 1, :a "baz2", :b "spam2"} {:id 2, :a "qux", :b "fred"}]
Если вам нужно много раз обращаться к вашим данным по id, другая идея - заменить ваш вектор хэш-карт хэш-картой хэш-карт с ключом :id
. Тогда вы можете легче assoc-in
независимо от порядка вещей.
user> (def new-my-vec (zipmap (map :id my-vec) my-vec))
#'user/new-my-vec
user> new-my-vec
{2 {:id 2, :a "qux", :b "fred"}, 1 {:id 1, :a "baz", :b "spam"}, 0 {:id 0, :a "foo", :b "bar"}}
user> (-> new-my-vec
(assoc-in [1 :a] "baz2")
(assoc-in [1 :b] "spam2"))
{2 {:id 2, :a "qux", :b "fred"}, 1 {:id 1, :a "baz2", :b "spam2"}, 0 {:id 0, :a "foo", :b "bar"}}
Ответ 3
Возможно, вы захотите взглянуть на array-map, который создает карту, поддерживаемую массивом, и вводится ключом вместо индекса: Я бы?