2016-10-11 4 views
1

Ich habe eine Karte mit einem Vektor der Karte wie folgt aus:aktualisieren verschachtelte Struktur von Karten und Vektoren

{:tags ["type:something" "gw:somethingelse"], 
:sources [{:tags ["s:my:tags"], 
      :metrics [{:tags ["a tag"]} 
         {:tags ["a noether tag" "aegn"]} 
         {:tags ["eare" "rh"]}]}]} 

Hinweis, dass es mehrere Quellen und mehrere Metriken sein.

Jetzt möchte ich die :metrics mit einer id aktualisieren, indem Sie auf den Wert der Tags.

Beispiel: Wenn ["a tag"] passt mit beispielsweise id 1 und ["a noether tag" "aegn"] mit id 2 Ich möchte die aktualisierte Struktur wie folgt aussehen:

{:tags ["type:something" "gw:somethingelse"], 
:sources [{:tags ["s:my:tags"], 
      :metrics [{:tags ["a tag"] 
         :id  1} 
         {:tags ["a noether tag" "aegn"] 
         :id  2} 
         {:tags ["eare" "rh"]}]}]} 

ich eine Funktion transform gemacht, die einen Tag eine ID konvertieren . Beispiel: (transform "a tag") gibt 1.

zurück

Nun, wenn ich versuche, die IDs mit einem Verständnis zu ergänzen, vermisse ich die alte Struktur (nur die inneren werden zurückgegeben) und mit assoc-in muss ich die Indizes im Voraus kennen.

Wie kann ich diese Transformation elegant durchführen?

+0

Bei der Bearbeitung Ihrer Frage, um die Klammern auszubalancieren, befürchte ich, dass ich die falsche Reparatur gemacht habe. Sowohl die ursprünglichen als auch die gewünschten Daten haben einen Vektor ': sources' mit nur einem Element. Ist es ein Vektor oder hätte ich die überflüssige öffnende eckige Klammer entfernen sollen? – Thumbnail

+0

Der Wert für den Schlüssel ': sources' ist ein Vektor von Maps. So wie es jetzt aussieht, sieht es gut für mich aus. –

Antwort

8

Ich würde von unten nach oben starten, Transformationsfunktion für :tags Eintrag machen, dann für :metrics und dann für :sources.

(defn transform [tags] (count tags)) 

user> (transform ["asd" "dsf"]) 
;;=> 2 

dann gelten Umwandlung der Metrik Eintrag:

(defn transform-metric [{:keys [tags] :as m}] 
    (assoc m :id (transform tags))) 

user> (transform-metric {:tags ["a noether tag" "aegn"]}) 
;;=> {:tags ["a noether tag" "aegn"], :id 2} 

jetzt

sagen wir unsere Funktion Tags durch Zählen ids Transformation nur produziert (nur zur Veranschaulichung, es später leicht geändert werden könnte) verwenden transform-metric Source-Eintrag zu aktualisieren:

(defn transform-source [s] 
    (update s :metrics #(mapv transform-metric %))) 

user> (transform-source {:tags ["s:my:tags"], 
         :metrics [{:tags ["a tag"]} 
            {:tags ["a noether tag" "aegn"]} 
            {:tags ["eare" "rh"]}]}) 

;;=> {:tags ["s:my:tags"], 
;; :metrics [{:tags ["a tag"], :id 1} 
;;    {:tags ["a noether tag" "aegn"], :id 2} 
;;    {:tags ["eare" "rh"], :id 2}]} 

und Der letzte Schritt besteht darin, die gesamten Daten zu transformieren:

(defn transform-data [d] 
    (update d :sources #(mapv transform-source %))) 

user> (transform-data data) 
;;=> {:tags ["type:something" "gw:somethingelse"], 
;; :sources [{:tags ["s:my:tags"], 
;;    :metrics [{:tags ["a tag"], :id 1} 
;;       {:tags ["a noether tag" "aegn"], :id 2} 
;;       {:tags ["eare" "rh"], :id 2}]}]} 

Also, wir sind hier fertig.

nun feststellen, dass transform-data und transform-source fast identisch sind, so können wir eine Nutzenfunktion machen, die solche Aktualisierung Funktionen erzeugt:

(defn make-vec-updater [field transformer] 
    (fn [data] (update data field (partial mapv transformer)))) 

Mit dieser Funktion können wir tiefe Transformationen wie folgt definieren:

(def transformer 
    (make-vec-updater 
    :sources 
    (make-vec-updater 
    :metrics 
    (fn [{:keys [tags] :as m}] 
     (assoc m :id (transform tags)))))) 

user> (transformer data) 
;;=> {:tags ["type:something" "gw:somethingelse"], 
;; :sources [{:tags ["s:my:tags"], 
;;    :metrics [{:tags ["a tag"], :id 1} 
;;       {:tags ["a noether tag" "aegn"], :id 2} 
;;       {:tags ["eare" "rh"], :id 2}]}]} 

und basierend auf dieser Transformatorkonstruktion Ansatz können wir eine nette Funktion machen, um die Werte in Vektoren-von-Karten-von-Vektoren-von-Karten-von-Vektoren zu aktualisieren ...Strukturen, mit beliebiger Verschachtelungsebene:

(defn update-in-v [data ks f] 
    ((reduce #(make-vec-updater %2 %1) f (reverse ks)) data)) 

user> (update-in-v data [:sources :metrics] 
        (fn [{:keys [tags] :as m}] 
        (assoc m :id (transform tags)))) 

;;=> {:tags ["type:something" "gw:somethingelse"], 
;; :sources [{:tags ["s:my:tags"], 
;;    :metrics [{:tags ["a tag"], :id 1} 
;;       {:tags ["a noether tag" "aegn"], :id 2} 
;;       {:tags ["eare" "rh"], :id 2}]}]} 

UPDATE

zusätzlich zu diesem Handbuch Ansatz gibt es eine fantastische lib specter gibt, die genau tut die gleiche Sache (und vieles mehr) genannt wird, aber ist offensichtlich universellere und verwendbar:

(require '[com.rpl.specter :as sp]) 

(sp/transform [:sources sp/ALL :metrics sp/ALL] 
       (fn [{:keys [tags] :as m}] 
       (assoc m :id (transform tags))) 
       data) 

;;=> {:tags ["type:something" "gw:somethingelse"], 
;; :sources [{:tags ["s:my:tags"], 
;;    :metrics [{:tags ["a tag"], :id 1} 
;;       {:tags ["a noether tag" "aegn"], :id 2} 
;;       {:tags ["eare" "rh"], :id 2}]}]} 
+1

aktualisiert meine Antwort mit dem ** Spectre ** Lib Beispiel – leetwinski

+0

Nizza! Das ist eine großartige Antwort. –

2
(use 'clojure.walk) 

(def transform {["a tag"]    1 
       ["a noether tag" "aegn"] 2}) 

(postwalk #(if-let [id (transform (:tags %))] 
      (assoc % :id id) 
      %) 
      data) 

Ausgang:

{:tags ["type:something" "gw:somethingelse"], 
:sources 
[{:tags ["s:my:tags"], 
    :metrics 
    [{:tags ["a tag"], :id 1} 
    {:tags ["a noether tag" "aegn"], :id 2} 
    {:tags ["eare" "rh"]}]}]} 
+0

nice approach, aber es könnte auch nicht benötigte ': tags' Werte umwandeln (zB top level: tags), wenn' transform' function einen id Wert für sie zurückgegeben hat ... – leetwinski

+0

Kann eine einfache Funktion verwenden (oder clojure verwenden. spec), um vor der Transformation auf Übereinstimmung zu prüfen. Z.B. (defn is-metric? [{: Keys [Stichworte Quellen Metriken]}] (und Tags (nicht (oder Quellen Metriken)))) – rmcv

+0

Ich mag diesen Ansatz! Vielen Dank. –

Verwandte Themen