2016-10-11 4 views
1

이 같은지도의 벡터지도를 가지고있다.업데이트 중첩 된 구조는

이제 태그 값을보고 :metricsid으로 업데이트하고 싶습니다.

예 : ["a tag"]가 ID 2로 예를 들어, ID 1과 일치하고, ["a noether tag" "aegn"] 있다면 업데이트 된 구조는 다음과 같이 할 : 내가 ID에 태그를 변환 할 수있는 기능 transform했다

{: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"]}]}]} 

. 예 : (transform "a tag")은 1을 반환합니다. 이제는 이해를 위해 ID를 추가하려고 할 때 이전 구조 (내적 요소 만 반환 됨)가 누락되어서 assoc-in으로 색인을 미리 알아야합니다.

이 변환을 어떻게 우아하게 수행 할 수 있습니까?

+0

브래킷의 균형을 맞추기 위해 질문을 편집 할 때 잘못된 수리를했을 수도 있습니다. 원래의 데이터와 원하는 데이터 모두에는 하나의 요소 만있는': sources' 벡터가 있습니다. 그것은 벡터입니까, 아니면 여분의 여는 대괄호를 제거 했어야합니까? – Thumbnail

+0

': sources' 키의 값은 맵의 벡터입니다. 그것이 지금 나에게 좋게 보이는 방법. –

답변

8

변환 기능을 :tags에 입력 한 다음 :metrics을 입력 한 다음 :sources을 입력하면됩니다. 이제 우리는 단지 태그를 계산하여 기능 ID를 생성 변환 가정 해 봅시다

(단지 그림을 위해, 나중에 쉽게 변경 될 수 있습니다) : 지금

(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} 

:

(defn transform [tags] (count tags)) 

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

다음 변환에게 통계 항목을 적용 소스 항목을 업데이트하려면 transform-metric을 사용하십시오.

(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}]} 

및 마지막 단계는 전체 데이터를 변환하는 것입니다 :

(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}]}]} 

그래서 여기서 끝났습니다.

지금 transform-datatransform-source 거의 동일하다는 것을 발견, 그래서 우리는 업데이트 기능을 생성하는 유틸리티 기능을 할 수 있습니다 : 우리는 다음과 같이 깊은 변환을 정의 할 수 있습니다이 기능을

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

을 :

(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}]}]} 

이 트랜스포머 구성 방법을 기반으로 우리는 vector of-maps-of-vectors-of-vectors의 값을 업데이트하는 멋진 함수를 만들 수 있습니다 ...임의의 중첩 수준과 구조 :

(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

이 매뉴얼 방식뿐만 아니라, 정확히 같은 일을 (그리고 더 많은) 거기 specter라는 환상적인 LIB가있다, 하지만 분명 더 보편적이고 사용할 수 :

(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

** 유령 ** lib 예제로 내 대답을 업데이트했습니다. – leetwinski

+0

니스! 이것은 훌륭한 대답입니다. –

2
(use 'clojure.walk) 

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

(postwalk #(if-let [id (transform (:tags %))] 
      (assoc % :id id) 
      %) 
      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"]}]}]} 
+0

좋은 접근 방법이지만,'transform' 함수가 그들에게 어떤 id 값을 반환했다면 불필요한': tags' 값 (예 : 최상위 레벨 : tags)을 변환 할 수도 있습니다 ... – leetwinski

+0

간단한 함수를 사용할 수 있습니다 (또는 clojure를 사용할 수 있습니다. spec)을 사용하여 변환을 수행하기 전에 적합성을 확인하십시오. 예 : (태그 : 소스 메트릭)] (및 태그 (소스 메트릭이 아님)) – rmcv

+0

나는이 접근 방식을 좋아합니다! 감사. –

관련 문제