【问题标题】:Trying to 'group' and flatten values from nested structure in clojure尝试从clojure中的嵌套结构中“分组”和展平值
【发布时间】:2014-02-02 01:06:41
【问题描述】:

又一个不安的夜晚试图弄清楚如何做到这一点。鉴于我已重新格式化的嵌套数据结构以查找格式错误的声明:

(def tm (list 
          {:a 1 :b 2 :c 3 :child (list 
               {:a 4 :b 5 :c 6 :child (list 
                     {:a 40 :b 50 :c 60 :child (list {:a nil :b nil :c nil})})}                                                                                               
               {:a 70 :b 80 :c 90 :child (list {:a nil :b nil :c nil})})} 
          {:a 400 :b 500 :c 600 :child (list {:a nil :b nil :c nil})}
          {:a 7 :b 8 :c 9 :child (list 
               {:a 10 :b 11 :c 12 :child (list {:a nil :b nil :c nil})})})
  )

并且遍历可以在下面的dg123示例中看到

我得到以下信息:

=>(roll-down tm :a)
((1 4 40) (1 70) (7 10))

但期待:

((1 4 40) (1 70) (400) (7 10))

【问题讨论】:

    标签: clojure


    【解决方案1】:

    这是一个基于拉链的解决方案

    (require '[clojure.zip :as z])
    
    (defn root-to-leaf-paths [t]
       (loop [t t paths []] 
         (cond 
           (z/end? t) paths
           (z/branch? t) (recur (z/next t) paths) 
           :leaf (recur (z/next t) (conj paths (z/path t))))))
    
    (defn roll-down [d kw]
       (->> (z/zipper :child :child nil {:child d})
            (root-to-leaf-paths)
            (map (comp rest (partial map kw)))))
    

    注意:您的tm 不是一棵树,而是一棵树的列表。所以,在这里我们首先通过将树列表放在一个父根{:child d} 下,将这个结构变成一棵树。然后我们从结果路径中删除它(comp rest ...)

    例子

    (roll-down tm :a)
    ;=> ((1 4 40) (1 70) (400) (7 10))
    
    (roll-down tm :b)
    ;=> ((2 5 50) (2 80) (500) (8 11))
    
    (roll-down tm :c)
    ;=> ((3 6 60) (3 90) (600) (9 12))
    

    【讨论】:

    • 也喜欢这个。尽管我认为建立初始系统状态将是一个“一次性”功能,但我会尝试并计时。
    【解决方案2】:

    短版使用mapcat:

    (defn roll-down [g k]
      (mapcat
       #(if-let [c (:child %)]
          (map (fn [x] (conj x (k %))) (roll-down c k))
          [[(k %)]])
       g))
    
    -> (roll-down tm :a)
    ([nil 40 4 1] [nil 70 1] [nil 400] [nil 10 7])
    

    此解决方案使用深度优先遍历并将nil 视为路径。如果您愿意,可以在结果上添加 removereverse

    【讨论】:

    • 经过多次迭代(包括删除)后,它的性能与拉链示例一样好。我也喜欢深度优先的结果,因为它实际上与我想要做的事情一致。为答案+1!
    • 顺便说一句:我最终使用 'subvec' 来消除 nil。
    【解决方案3】:

    这是一种不使用可变状态的更实用的方法:

    (defn roll-down-helper [c kw]
      (when-let [root-val (kw c)]
        (cons root-val
              (filter (comp not nil?)
                      (for [node (:child c)]
                        (roll-down-helper node kw))))))
    
    (defn get-paths [[x & ys]]
      (if (empty? ys)
        (list (list x))
        (for [y ys]
          (flatten (cons x y)))))
    
    
    (defn roll-down [c kw]
      (apply concat
       (map (comp get-paths #(roll-down-helper % kw)) c)))
    

    然后您可以执行以下操作:

    user> (roll-down tm :a)
    ((1 4 40) (1 70) (400) (7 10))
    user> (roll-down tm :b)
    ((2 5 50) (2 80) (500) (8 11))
    user> (roll-down tm :c)
    ((3 6 60) (3 90) (600) (9 12))
    

    【讨论】:

    • 我将此标记为已回答,但是我要向 {:a 4 b: 5 :c 6} 添加更多子项,它不遵循分支。
    • @Frank C 如果您将:c 60 末尾的} 移动到其:child 列表的末尾,就像您的tm 数据结构中的所有其他节点一样?之前没见过,不过貌似那部分数据结构格式不正确。
    • 我已经重新格式化并触摸了问题中现在显示的结构,并且结果略有不同但不完整。计数结构按预期返回 3。
    • @FrankC。我更新了我的答案,它现在应该可以正常工作了。 get-paths 没有正确处理传入的列表只有一个元素的情况。
    • 那是赢家!非常感谢,我得研究一段时间才能理解它。谢谢!
    【解决方案4】:

    您可以简单地将提取器映射到您的数据上

    (defn get-as [tm] 
      (let [results (atom [])]
        (clojure.walk/prewalk
         #(do (if-let [uid (:a %)] 
                (swap! results conj :a (:a %))) 
              %)
         tm)
        @results))
    
    (map get-as tm) ; => ([:a 1 :a 4] [:a 7 :a 10])
    

    【讨论】:

    • 我已经编辑了这个问题,因为它表达得不够清楚。
    猜你喜欢
    • 1970-01-01
    • 2019-01-21
    • 2019-10-31
    • 2014-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-01
    相关资源
    最近更新 更多