我正在尝试从嵌套数据结构中获取值的索引路径。
我写了一些代码,但它无法正常工作
(defn helper
[val form c index]
(loop [f form
i index
l 0]
(if (seq f)
(if (or (list? (first f)) (vector? (first f)))
(helper val (first f) (conj c l) (inc i))
(if (= val (first f))
(conj c l)
(recur (rest f) (inc i) (inc l))))
nil)))
(defn find-indexes
[val form c index]
(when (seq form)
(if-let [result (helper val form c index)]
result
(find-indexes val (rest form) c (inc index)))))
(defn find-index-route
[val form]
(find-indexes val form [] 0))
当前行为:
(find-index-route :my-key '(1 2 ("a" :my-key))) ;=> [2 1] "Works"
(find-index-route :my-key '(1 2 ("a" ["b" 3 :my-key]))) ;=> [2 1 2] "Works"
(find-index-route :my-key '(1 2 ("a" ["b" 3 () :my-key]))) ;=> nil "Does NOT Work"
(find-index-route :my-key '(1 2 ("a" [] ["b" 3 :my-key]))) ;=> nil "Does NOT Work"
(find-index-route :my-key '(1 2 [] ("a" ["b" 3 :my-key]))) ;=> [0 1 2] "It has to be [3 1 2]"
问题是如果函数命中空列表或向量,然后才能找到它返回的值 nil或0 (仅适用于第一级)
我需要的行为:
;=> [indexes...]
(find-index-route :my-key '(1 2 :my-key)) ;=> [2]
(find-index-route :my-key '(1 2 "a" :my-key "b")) ;=> [3]
(find-index-route :my-key '(1 2 [:my-key] "c")) ;=> [2 0]
(find-index-route :my-key '(1 2 [3 [:my-key]])) ;=> [2 1 0]
(find-index-route :my-key '(1 2 [3 [[] :my-key]])) ;=> [2 1 1]
(find-index-route :my-key '(1 2 [3 [4 5 6 (:my-key)]])) ;=> [2 1 3 0]
(find-index-route :my-key '(1 2 [3 [[]]])) ;=> nil or []
答案 0 :(得分:3)
(defn find-index-route [x coll]
(letfn [(path-in [y]
(cond
(= y x) '()
(coll? y) (let [[failures [success & _]]
(->> y
(map path-in)
(split-with not))]
(when success (cons (count failures) success)))))]
(path-in coll)))
示例:
(find-index-route :my-key '(1 2 :my-key)) ;=> [2]
=> (2)
(find-index-route :my-key '(1 2 "a" :my-key "b")) ;=> [3]
=> (3)
(find-index-route :my-key '(1 2 [:my-key] "c")) ;=> [2 0]
=> (2 0)
(find-index-route :my-key '(1 2 [3 [:my-key]])) ;=> [2 1 0]
=> (2 1 0)
(find-index-route :my-key '(1 2 [3 [[] :my-key]])) ;=> [2 1 1]
=> (2 1 1)
(find-index-route :my-key '(1 2 [3 [4 5 6 (:my-key)]])) ;=> [2 1 3 0]
=> (2 1 3 0)
(find-index-route :my-key '(1 2 [3 [[]]])) ;=> nil or []
=> nil
考虑表现
split-with
使用take-while
和drop-while
扫描序列两次,以生成两个惰性序列。如果性能很差,您可以编写一个drop-while
版本,它还会告诉您已丢弃的项目数量:
(defn counted-drop-while [pred coll]
(loop [takes 0, tail coll]
(if (and (seq tail) (pred (first tail)))
(recur (inc takes) (rest tail))
[takes tail])))
它只进行一次扫描。我们可以轻松地调整find-index-route
来使用它:
(defn find-index-route [x coll]
(letfn [(path-in [y]
(cond
(= y x) '()
(coll? y) (let [[fail-count [success & _]]
(->> y
(map path-in)
(counted-drop-while not))]
(when success (cons fail-count success)))))]
(path-in coll)))
......结果相同。
答案 1 :(得分:3)
这个问题对于Spectre库来说似乎是完美的,它是关于导航和转换嵌套数据结构的。我问Nathan Marz关于昨天Clojurians可能解决这个问题的方法 - 请参阅他原来的答案here:
(defn find-index-route [v data]
(let [walker (recursive-path [] p
(if-path sequential?
[INDEXED-VALS
(if-path [LAST (pred= v)]
FIRST
[(collect-one FIRST) LAST p])]))
ret (select-first walker data)]
(if (vector? ret) ret [ret])))
请注意,如果:my-key不存在,则返回[nil]。您可以通过将最后一个“if”部分更改为此来轻松修改它以返回nil:
(if (or (vector? ret) (nil? ret)) ret [ret])
答案 2 :(得分:1)
我制作的版本找到全部路线
。(defn find-index-route
"find all routes to some value"
([k x] (find-index-route k x []))
([k x p]
(cond
(= k x) [p]
(coll? x) (->> (if (map? x) x (vec x))
(reduce-kv (fn [acc i v]
(into (vec (find-index-route k v (conj p i))) acc)) []))
:else nil)))
编辑:也适用于地图
(find-index-route :my-key '{:bar 33
:foo [{:my-key ("0" :my-key)}]
"bar" ["key" {:foo ([:my-key])}]})
;=> [["bar" 1 :foo 0 0] [:foo 0 :my-key 1]]
答案 3 :(得分:0)
我还找到了另一个解决方案:
(defn find-index-route
[x form]
(letfn [(get-nodes [form]
(tree-seq coll? identity form))
(get-tree [form]
(rest (get-nodes form)))
(get-level [form]
(if (or (not (coll? form)) (not (seq form)))
0
(count (filter coll? (get-nodes form)))))
(get-result [x form]
(reduce (fn [v form]
(let [[idx lvl _] (last v)
form-lvl (get-level form)
contains? ((set (get-nodes form)) x)]
(conj v [(if (or (not idx) (< form-lvl lvl)) 0 (+ idx 1))
form-lvl
contains?]))) [] (get-tree form)))
(get-indices [x form]
(map (fn [[idx _ _]] idx)
(filter
(fn [[_ _ contains?]] contains?) (get-result x form))))]
(get-indices x form)))