首页 > 解决方案 > 在 Clojure 中过滤来自 Atom 的映射列表

问题描述

我正在开发一个迷你社交媒体 API,允许用户插入新的个人资料,将两个个人资料连接在一起(如朋友),然后根据“我朋友的朋友”规则接收推荐。

现在我正在尝试为 Profile 创建 API。

我有一个包含地图列表的原子,每个配置文件一个。

(def profiles (atom ()))

(defn create [request]
 (swap! profiles conj {:id (get-in request [:body "id"])
                    :name (get-in request [:body "name"])
                    :age (get-in request [:body "age"])
                    :recommendable (get-in request [:body "recommendable"])
                    :friends (list) 
})
(created "")
)

当我偶然发现一个问题时,我试图为 API 的 GET http 动词开发 find-by-id。如何从所述列表中的地图中获取值,以便我可以对其应用函数?

例如,在这里我试图使用过滤器函数只返回包含给定 id 的地图。但我不断收到错误消息:

(defn find-by-id [id]
  (filter #(= (:id %) id) profiles)
)

Dont know how to create ISeq from: clojure.lang.Atom

在我看来,过滤器不适用于 Atom。

同样的事情发生在删除:

(defn delete-by-id [id]
 (swap! profiles (remove #(= (:id %) id) profiles))
)

当我尝试使用@profiles 时,我得到一个空数组。更糟糕的是,当我尝试使用 REPL 的过滤器功能时,它工作得很好。

这让我想知道我在这里错过了什么。

谁能告诉我发生了什么?

提前致谢。

标签: clojure

解决方案


第一个失败了,因为正如它所说,原子不是filter预期的序列。

您需要先从原子中取出序列filter

; I'm dereferencing the atom using @ to get the list of profiles that it holds
(defn find-by-id [id]
  (filter #(= (:id %) id) @profiles))

但请注意,这不是最佳选择。您依赖的状态profiles可能会在看似随机的时间发生变化(如果您有异步进程swap!ping 它)。这可能会使调试复杂化,因为在数据传递到filter. 函数依赖于原子也是不好的profiles,因为这与它的功能无关,你以后可能会改变你的设计。让这个函数完全依赖于它的参数并且对原子一无所知,这将是更多的未来证明:

(defn find-by-id [id profiles]
  (filter #(= (:id %) id) profiles))

; Then call it like this. I renamed your atom here
(find-by-id some-id @profile-atom)

您的第二个示例失败,因为swap!接受一个函数作为其第二个参数。我认为您的意思是使用reset!,它会改变原子的值,而不管它以前是什么:

(defn delete-by-id [id]
  (reset! profiles (remove #(= (:id %) id) @profiles)))

虽然,这也不是最优的。如果要根据先前的状态更新原子,请swap!改用并提供更新函数:

(defn delete-by-id [id]
  (swap! profile-atom (fn [profiles] (remove #(= (:id %) id)) profiles)))

或者,稍微简洁一点:

(defn delete-by-id [id]
  (swap! profile-atom (partial remove #(= (:id %) id))))

我正在部分申请remove制作功能。原子的旧状态作为最后一个参数传递给remove.


推荐阅读