Gereksiz anahtarları büyük clojure haritasında filtrelemek

Clojure'da gerçekten büyük bir eşek iç içe geçmiş bir harita aldım ve ön uçta bulunmaması gereken anahtarlara basmanın en aptalca yolunu arıyorum (evet, bu clojure servisi arka uçta çalışıyor).

Veri yapısı şuna benziyor:

(def data
  {:a 1
   :b 2
   :c 3
   :d [{:e 5}
       {:f 6
        :g {
            :h 8
            :i 9
            :j 10}
        :l [{
             :m 11
             :n 12
             :p {:q 13
                 :r 14
                 :s 15
                 }}
            {:m 16
             :n 17
             :p {:q 18
                 :r 19
                 :s 20
                 }}]}]})

As you can see, I got a map with keys, whereby some keys got lists with maps, which have some lists again...so I know -> not pretty.

AMA ... almak istemediğim verileri tanımlamanın bir yolu var, böylece istemediğim her anahtarın filtrelenmesi sağlanabiliyor mu?

Teşekkürler

2
Girişten ne elde etmek istiyorsunuz? Büyük bir haritayı yapıştırdın ve sadece bir kısmını istediğini söyledin, ama hangi kısmı? Neyin dahil edilmeye değer olduğuna nasıl karar veriyorsunuz?
katma yazar amalloy, kaynak
Belki, ama ben en iyi yol bu mu? Yinelemeli olarak da yapabilirdim ... ama belki bir şekilde verilere uygulayabileceğim bir şema yazılabilir.
katma yazar Tobias K., kaynak

5 cevap

başka bir yöntem, herhangi bir harici lib kullanmadan, clojure.walk kullanarak:

(defn remove-deep [key-set data]
  (clojure.walk/prewalk (fn [node] (if (map? node)
                                     (apply dissoc node key-set)
                                     node))
                        data))


user> (remove-deep [:i :l] data)
;;=> {:a 1, :b 2, :c 3, :d [{:e 5} {:f 6, :g {:h 8, :j 10}}]}

user> (remove-deep [:f :p] data)
;;=> {:a 1, :b 2, :c 3, :d [{:e 5} {:g {:h 8, :i 9, :j 10}, :l [{:m 11, :n 12} {:m 16, :n 17}]}]}

Pre/postwalk, sahip olduğunuz kullanımın kesin hali için oradadır: heterojen koleksiyonlardan aşağıya yürüyün, gerekirse değerleri dönüştürün

7
katma
Evet, bu 2-vec'leri (MapEntry çiftleri) aramaktan daha basittir.
katma yazar Alan Thompson, kaynak
@slipset, tree-seq geçiş için uygun, ancak değişiklik için kullanışsız
katma yazar leetwinski, kaynak

En basit olanı, clojure.walk/postwalk kullanmak için 'dir . Sanırım "kaldır: ben sadece: f" nin bir çocuğuysa, herhangi bir anahtar birleşimi için endişelenmenize gerek yok.

İşte bir örnek:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test)
  (:require [clojure.walk :as walk]))

(def data
  {:a 1
   :b 2
   :c 3
   :d [{:e 5}
       {:f 6
        :g {
            :h 8
            :i 9
            :j 10}
        :l [{
             :m 11
             :n 12
             :p {:q 13
                 :r 14
                 :s 15
                 }}
            {:m 16
             :n 17
             :p {:q 18
                 :r 19
                 :s 20
                 }}]}]})

(defn remove-keys [data keys]
  (let [proc-node  (fn [node]
                     (spyx node))
        result (walk/postwalk proc-node data) ]
    (spyx-pretty result)))

(def bad-keys #{:b :f :i :p :n})

(dotest
  (remove-keys data bad-keys))

Bu, postwalk 'ın özyinelemeli işlemesini gösterir:

Testing tst.demo.core
node => :a
node => 1
node => [:a 1]
node => :b
node => 2
node => [:b 2]
node => :c
node => 3
node => [:c 3]
node => :d
node => :e
node => 5
node => [:e 5]
node => {:e 5}
node => :f
node => 6
node => [:f 6]
node => :g
node => :h
node => 8
node => [:h 8]
node => :i
node => 9
node => [:i 9]
node => :j
node => 10
node => [:j 10]
node => {:h 8, :i 9, :j 10}
node => [:g {:h 8, :i 9, :j 10}]
node => :l
node => :m
node => 11
node => [:m 11]
node => :n
node => 12
node => [:n 12]
node => :p
node => :q
node => 13
node => [:q 13]
node => :r
node => 14
node => [:r 14]
node => :s
node => 15
node => [:s 15]
node => {:q 13, :r 14, :s 15}
node => [:p {:q 13, :r 14, :s 15}]
node => {:m 11, :n 12, :p {:q 13, :r 14, :s 15}}
node => :m
node => 16
node => [:m 16]
node => :n
node => 17
node => [:n 17]
node => :p
node => :q
node => 18
node => [:q 18]
node => :r
node => 19
node => [:r 19]
node => :s
node => 20
node => [:s 20]
node => {:q 18, :r 19, :s 20}
node => [:p {:q 18, :r 19, :s 20}]
node => {:m 16, :n 17, :p {:q 18, :r 19, :s 20}}
node => [{:m 11, :n 12, :p {:q 13, :r 14, :s 15}} {:m 16, :n 17, :p {:q 18, :r 19, :s 20}}]
node => [:l [{:m 11, :n 12, :p {:q 13, :r 14, :s 15}} {:m 16, :n 17, :p {:q 18, :r 19, :s 20}}]]
node => {:f 6, :g {:h 8, :i 9, :j 10}, :l [{:m 11, :n 12, :p {:q 13, :r 14, :s 15}} {:m 16, :n 17, :p {:q 18, :r 19, :s 20}}]}
node => [{:e 5} {:f 6, :g {:h 8, :i 9, :j 10}, :l [{:m 11, :n 12, :p {:q 13, :r 14, :s 15}} {:m 16, :n 17, :p {:q 18, :r 19, :s 20}}]}]
node => [:d [{:e 5} {:f 6, :g {:h 8, :i 9, :j 10}, :l [{:m 11, :n 12, :p {:q 13, :r 14, :s 15}} {:m 16, :n 17, :p {:q 18, :r 19, :s 20}}]}]]
node => {:a 1, :b 2, :c 3, :d [{:e 5} {:f 6, :g {:h 8, :i 9, :j 10}, :l [{:m 11, :n 12, :p {:q 13, :r 14, :s 15}} {:m 16, :n 17, :p {:q 18, :r 19, :s 20}}]}]}
result => 
{:a 1,
 :b 2,
 :c 3,
 :d
 [{:e 5}
  {:f 6,
   :g {:h 8, :i 9, :j 10},
   :l
   [{:m 11, :n 12, :p {:q 13, :r 14, :s 15}}
    {:m 16, :n 17, :p {:q 18, :r 19, :s 20}}]}]}

Haritaların ilk önce [: n 17] gibi anahtar/değer çiftlerinin vektörlerine dönüştürüldüğünü görebilirsiniz. Öyleyse, böyle bir 2-vec aldığınızda, ilk öğeye bakın ve beğenmediyseniz bir nil döndürün:

(defn len-2-vec? [node]
  (and (sequential? node)
    (= 2 (count node))))

(defn remove-keys [data bad-keys]
  (let [proc-node (fn [node]
                    (if (and (len-2-vec? node)
                          (contains? bad-keys (first node)))
                      (do
                        (spyx :removed node)
                        nil)
                      node))
        result (walk/postwalk proc-node data) ]
    (spyx-pretty result)))

(def bad-keys #{:b :f :i :p :n})

(dotest
  (remove-keys data bad-keys))

ve çıktı:

Testing tst.demo.core
:removed    node => [:b 2]
:removed    node => [:f 6]
:removed    node => [:i 9]
:removed    node => [:n 12]
:removed    node => [:p {:q 13, :r 14, :s 15}]
:removed    node => [:n 17]
:removed    node => [:p {:q 18, :r 19, :s 20}]

(remove-keys data bad-keys) => 
{:a 1, 
 :c 3, 
 :d [{:e 5} 
     {:g {:h 8, 
          :j 10}, 
      :l [{:m 11}
          {:m 16}]}]}

Ran 2 tests containing 0 assertions.
0 failures, 0 errors.

Clojure CheatSheet 'i unutmayın.

Here is the doc for spyx.

4
katma

If you want to filter for the top-level keys only you can use select-keys

Derinden iç içe geçmiş anahtarları kaldırmak istiyorsanız, hayaleti kullanabilirsiniz. Örneğin, : h altındaki tüm değerleri : g altındaki vektördeki : d altındaki her öğenin altına yazmanız yeterlidir:

user> (setval [:d ALL :g :h] NONE data)
4
katma

Bu gerekenden daha fazla "el kaldırma" kullanıyor olabilir, ancak basit bir özyinelemeli işlevi bu iyi idare eder:

(defn filter-nested [root keys-to-remove]
  (let [should-remove? (set keys-to-remove)

        ; A recursive function to search through the map
        f (fn rec [node]
            (reduce-kv (fn [acc k v]
                         (cond
                           ; If it's in the set, remove the key from the node
                           (should-remove? k) (dissoc acc k)

                           ; If the value is a map, recursively search it too
                           (map? v) (assoc acc k (rec v))

                           ; If it's a vector, map a recursive call over the vector
                           (vector? v) (assoc acc k (mapv rec v))

                           ; Else do nothing
                           :else acc))
                       node
                       node))]
    (f root)))

(filter-nested data #{:l})
=> {:a 1, :b 2, :c 3, :d [{:e 5} {:f 6, :g {:h 8, :i 9, :j 10}}]}

Açıklayıcı yorumları göz önüne aldığınızda, göründüğü kadar büyük değildir. f (dahili olarak rec olarak adlandırılmıştır), verilen anahtar listesinde bulunan haritadan disoc tuşlarını bulunan haritadan silmelerini sağlayan özyinelemeli bir işlevdir. Bulduğu değer bir harita veya vektör olduğunda, onları da aramaya başlar.

3
katma

Kara liste kullanmak yerine, bir tür beyaz listeye sahip olmak istedik. Üretimde kara liste ile çalışmak pek iyi bir fikir değildir - eğer bir nedenden dolayı cevap nesnesi uzatılabilirse. Bu nedenle şimdi https://github.com/metosin/spec-tools adresini kullanıyoruz. kod> strip-extra-keys-transformer gibi:

(ns sexy.helper.transformer
  (:require [spec-tools.core :as st]
            [spec-tools.data-spec :as ds]))

(def my-abc {:a "12345"
               :b "00529"
               :c [{:d "Kartoffel"
                    :e 5}
                   {:d "Second Item"
                    :e 9999}]})

(def the-abc
  {:a string?
   :c [{:d string?}]})

(def abc-spec
  (ds/spec ::abc the-abc))

(st/conform abc-spec my-abc st/strip-extra-keys-transformer)
1
katma