【问题标题】:Filter a list of maps from an Atom in Clojure在 Clojure 中过滤来自 Atom 的映射列表
【发布时间】:2018-08-21 22:01:44
【问题描述】:

我正在开发一个迷你社交媒体 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


    【解决方案1】:

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

    filter it 之前,您需要先从原子中取出序列:

    ; 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

    【讨论】:

    • 非常感谢您的帮助。但是 find-by-id 一直没有给我任何结果。并且删除不会从列表中删除地图。此外,您建议我将配置文件列表转换为我的函数的参数,但由于这是一个 API,我的 handler.clj 文件只有路由,整个代码都在 profile.clj 文件中。如果我将配置文件作为参数传递,我将在哪里声明它?由于将其留在 profile.clj 中会使它在 handler.clj 中无法访问,并且如果它与我的创建函数相反,将无法更新配置文件列表。
    • @LeonardoMarinho 对于过滤和删除不起作用,ID 必须不是您要搜索的内容。您需要仔细检查您传递的 ID 与地图中的 ID。对于第二个问题,您可以将其包装在另一个函数中。像(fn [profile-atom] (find-by-id some-id @profile-atom))) 这样的东西。我不确定这是否能回答您的问题。
    猜你喜欢
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 2012-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-22
    • 1970-01-01
    相关资源
    最近更新 更多