如何在Clojure中将相同的值获取到一个更扁平的数据结构中?

3

我有一个数据结构:

(def params
  {:site1
    {:index-element
       {:date  "2012-10-10"
        :title "Hello"}
     :subpage-element
        {:time     "9:00"
         :location "Toronto"}}
   :site2 …})

我希望将它转换成更扁平的数据结构。

(def new-params  
  {1
    {:name     :site1
     :date     ["2012-10-10" :index-element]
     :title    ["Hello"      :index-element] 
     :time     ["9:00"       :subpage-element]
     :location ["Toronto"    :subpage-element]}
   2 …})

为了得到我想要的结果,我进行了以下操作:
(mapv #(-> params % :index-element vals)
      (keys params))

如何使用new-params获得相同的结果?
2个回答

4

以下是三种方式:

;;; 1.
(reduce-kv (fn [res siteno m]
             (->> (vals m)
                  (filter #(and (vector? %)
                                (identical? :index-element (peek %))))
                  (map first)
                  (conj res)))
           []
           new-params)

;;; 2.
(->> new-params
     (vals)
     (map vals)
     (map (partial filter #(and (vector? %)
                                (identical? :index-element (peek %)))))
     (mapv (partial map first)))

;;; 3. (better than 2., but only for Clojure >= 1.5)
(require '[clojure.core.reducers :as r])

(->> new-params
     (vals)
     (r/map vals)
     (r/map (partial filter #(and (vector? %)
                                  (identical? :index-element (peek %)))))
     (r/map (partial map first))
     (into []))

切换到 mapv���在1.中的匿名函数中,在2.和3.的最后一个 partial 中)以获得向量的向量,而不是惰性序列的向量(从而使内部集合的构造非惰性——这可能是有意义的)。

原始数据结构实际上更适合这种访问方式;使用更扁平的映射将无法避免线性搜索。这可能会成为性能问题,具体取决于访问模式和实际数据。


很棒的帖子,谢谢!我真的很惊讶它这么复杂。我猜你是对的,即使扁平映射倾向于更清晰,但在这种情况下,它会使得获取与:index-element相关联的值更加复杂。 - leontalbot

3

一种选择是使用 for 循环(在我看来这样更易读):

(->> (for [[i site] new-params]
       (for [ [s-k s-v] site
              :when (not= s-k :name)
              :let [[k [v v-k]] [s-k s-v]]
              :when (= v-k :index-element)]
         v))
     (into []))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接