Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Clojure中有条件地初始化映射的元素_Clojure - Fatal编程技术网

在Clojure中有条件地初始化映射的元素

在Clojure中有条件地初始化映射的元素,clojure,Clojure,我正在寻找一种最好的方法,有条件地避免在初始化/定义映射时将元素添加到映射中。在这种情况下,如果键的值为nil,我希望避免向映射中添加元素 (defn create-record [data] (let [res { :username (data :username) :first-name (get-in data [:user-info :name :first]) :last-name (get-in data [:user-info :name :last]

我正在寻找一种最好的方法,有条件地避免在初始化/定义映射时将元素添加到映射中。在这种情况下,如果键的值为nil,我希望避免向映射中添加元素

(defn create-record [data]
  (let [res {
    :username (data :username)
    :first-name (get-in data [:user-info :name :first])
    :last-name (get-in data [:user-info :name :last])
    :gender (get-in data [:user-info :sex])
   }])
)
如果进入结果为零(数据中的性别字段不存在),我不想将性别添加到地图中。当我创建地图时,有没有办法做到这一点?创建映射后,我可以删除所有值为零的键,但在某些情况下,我希望某些键具有零值,而其他键如果具有零值,则根本不在映射中。

以下是一个尝试:

(defn exclude-nils-for [m kw-set] 
    (apply hash-map (apply concat (remove (fn [[k v]] (and (kw-set k) (nil? v))) m))))
测试:

user> (exclude-nils-for {:gender "m" :name "Thomas" :age "24"} #{})
{:age "21", :gender "m", :name "Thomas"}
user> (exclude-nils-for {:gender "m" :name "Thomas" :age "24"} #{:name})
{:age "21", :gender "m", :name "Thomas"}
user> (exclude-nils-for {:gender "m" :name nil :age "24"} #{:name})
{:age "21", :gender "m"}
user> (exclude-nils-for {:gender "m" :name nil :age nil} #{:age})
{:gender "m", :name nil}

构建映射并基于谓词(此处--
nil?
)分解要施加条件的键可能是最简单的方法(注意,此函数仅测试作为参数明确提及的键;未提及的键永远不会删除,无论附加到它们的值是否满足谓词):

在REPL上:

user> (dissoc-when nil? {:foo nil :bar true :quux nil} :foo :bar)
{:quux nil, :bar true}
虽然一般来说,如果您希望使用大量表示某一特定类型的真实世界实体的映射,您可能希望使用记录,然后您可以在从输入映射提取值的阶段跳过所有
nil
s,因为记录作为映射查看时,似乎总是包含与其字段对应的键。例如

(defrecord Person [username first-name last-name])
然后,您可以考虑映射之间的“模式转换”逻辑:

(defn translate-map
  "Transforms the data map in accordance with the spec in table.
   Skips nil-valued entries."
  [data table]
  (->> table
       (keep (fn [[to from]]
               (when-let [from-val (get-in data from)]
                 [to from-val])))
       (into {})))
现在,您的
create record
函数变成了
translate map
map->Person
的组合:

(defn create-person [data]
  (map->Person
   (translate-map data {:username [:username]
                        :first-name [:user-info :name :first]
                        :last-name [:user-info :name :last]
                        :gender [:user-info :sex]})))
如果您确实更喜欢使用常规贴图,则可以使用类似以下内容的内容来实现等效输出:

(defn create-person [data]
  (merge (zipmap [:username :first-name :last-name] (repeat nil))
         (translate-map data {:username [:username]
                              :first-name [:user-info :name :first]
                              :last-name [:user-info :name :last]
                              :gender [:user-info :sex]})))
在REPL(Clojure 1.3中的记录版本):


您可以定义字段以及哪些字段是可选的:

(def fields
[[:username   [:username]]
 [:first-name [:user-info :name :first]]
 [:sex        [:user-info :sex]          true]])
然后编写一个函数来使用该信息:

(defn create-record [data keys]
  (->>
    (for [[n k ignore-nil?] keys 
            :let [v (get-in data k)] 
            :when (or (not ignore-nil?) v)]
      [n v])
    (into {})))
它的工作原理如下:

; If :sex is missing don't create a field
user=> (create-record {:username "dr" :user-info { :name {:first "Dave"} }} fields)
{:username "dr", :first-name "Dave"}

user=> (create-record {:username "dr" :user-info { :name {:first "Dave"} :sex :m }} fields)
{:username "dr", :first-name "Dave", :sex :m}

; If :first is missing, create a nil field
user=> (create-record {:username "dr" :user-info { :name {} :sex :m }} fields)
{:username "dr", :first-name nil, :sex :m}

根据需要修改:)

您可以执行以下操作

(let [not-nils #{:gender}]
  (defn create-record [data]
    (into {} (for [[k v] {:username (data :username)
                          :first-name (get-in data [:user-info :name :first])
                          :last-name (get-in data [:user-info :name :last])
                          :gender (get-in data [:user-info :sex])}
                   :when (not (and (nil? v) (not-nils k)))]
               [k v]))))

对于这些可选参数,我将使用
merge
when let
的组合

核心思想是为每个可选参数合并一个元素映射或nil。在nil中合并将不起任何作用,因此您不会在地图中看到nil

(defn create-record [data]
  (let [res (merge {:username (data :username)
                    :first-name (get-in data [:user-info :name :first])
                    :last-name (get-in data [:user-info :name :last])}
                   (when-let [gender (get-in data [:user-info :sex])]
                     {:gender gender}))]
    res))

根据您需要执行此操作的频率,我建议您围绕when let编写一个简短的宏或函数,以使代码更简洁。

您可能希望将
展平
替换为
应用concat
,否则更复杂的值会导致问题(在第一个示例中,尝试将
:foo[12]
添加到映射中). (或者打开一个级别的paren并使其应用(comp hash map concat))此外,我还将为不排除
nil
值的情况添加一个测试(尽管这确实有效)。完成。谢谢你的评论!谢谢,这很好-但是为什么要使用多余的
(让[res…res]
wrapper?@Alex我一直在尽可能地保持代码与他的代码相似。我假设他可能在作业后对结果做了其他事情,所以我把它放在那里了。你是对的,虽然没有必要。啊,谢谢,有道理@deterb。我担心我遗漏了什么。旁白:“性别”!=“性”
(defn create-record [data]
  (let [gender (get-in data [:user-info :sex])]
    (->> {:username (data :username)
          :first-name (get-in data [:user-info :name :first])
          :last-name (get-in data [:user-info :name :last])}
         (#(if gender (assoc % :gender gender) %)))))
(defn create-record [data]
  (let [res (merge {:username (data :username)
                    :first-name (get-in data [:user-info :name :first])
                    :last-name (get-in data [:user-info :name :last])}
                   (when-let [gender (get-in data [:user-info :sex])]
                     {:gender gender}))]
    res))
(defn create-record [data]
  (let [gender (get-in data [:user-info :sex])]
    (->> {:username (data :username)
          :first-name (get-in data [:user-info :name :first])
          :last-name (get-in data [:user-info :name :last])}
         (#(if gender (assoc % :gender gender) %)))))
(cond-> {:username   (data :username)
         :first-name (get-in data [:user-info :name :first])
         :last-name  (get-in data [:user-info :name :last])}
    (get-in data [:user-info :sex]) (assoc :gender (get-in data [:user-info :sex])))