Clojure - фильтр вложенной карты на самом внутреннем уровне - PullRequest
0 голосов
/ 11 января 2019

что будет лучшим способом фильтрации следующей вложенной карты, сохраняя структуру вложенной карты. В моем примере Алиса и Боб могут быть дублированы, например один и тот же сотрудник может работать одновременно на нескольких заводах.

(def universe
  {:customer1
    {:plant1
      { "Alice" {:age 35 :sex "F"}
        "Bob" {:age 25 :sex "M"}}
     :plant2 {}} 
   :customer2 {}
  })

Я хотел бы, например, отфильтровать по возрасту> 30 и вернуть ту же структуру карты. В идеале это будет работать для любой глубины вложенной карты с фильтрацией на самом внутреннем уровне. Ожидаемый результат:

(def universe
  {:customer1
    {:plant1
      { "Alice" {:age 35 :sex "F"}
        }
     :plant2 {}} 
   :customer2 {}
  })

Я посмотрел на вложенную карту фильтра clojure, чтобы вернуть ключи, основанные на значениях внутренней карты , но это не похоже на решение моей проблемы. Спасибо,

Ответы [ 3 ]

0 голосов
/ 11 января 2019

Спасибо @akond за ответ, чтение кода заставило меня задуматься о не призрачном решении. Тем не менее, немного удивлен, что в этом случае нет простого способа применения filter.

(defn recursive-filter [pred k m]
    (letfn [(pair-filter [pair] (if (pred (k (second pair))) pair nil))]
    (into {}
        (for [a m]
            (if (empty? (second a))
                [(first a) {}]
                (if (contains? (second a) k)
                    (pair-filter a)
                    [(first a) (recursive-filter pred k (second a))]))))))
0 голосов
/ 11 января 2019

Ваши данные немного необычны, так как обычно можно ожидать, что :customer1, :customer2 и т. Д. Будут разными записями в векторе. Для таких полуструктурированных данных я бы рассмотрел postwalk:

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

(def universe
  {:customer1
    {:plant1
      {"Alice" {:age 35 :sex "F"}
       "Bob"   {:age 25 :sex "M"}}
      :plant2 {}}
   :customer2 {}})

(def age-of-wisdom 30)

(defn wisdom-only
  [form]
  (let [filter-entry? (when (map-entry? form)
                        (let [[-name- details]   form
                              age                (:age details)] ; => nil if missing
                          (and age ; ensure not nil
                            (< age age-of-wisdom))))]
    (if filter-entry?
      {}
      form)))

(walk/postwalk wisdom-only universe) => 
  {:customer1 
    {:plant1 
      {"Alice" {:age 35, :sex "F"}}
     :plant2 {}}
   :customer2 {}}
0 голосов
/ 11 января 2019

Очень похоже на один из предыдущих вопросов:

(use '[com.rpl.specter])
(let [input          {:customer1
                                 {:plant1
                                          {"Alice" {:age 35 :sex "F"}
                                           "Bob"   {:age 25 :sex "M"}}
                                  :plant2 {}}
                      :customer2 {}}
      desired-output {:customer1
                                 {:plant1 {"Alice" {:age 35 :sex "F"}}
                                  :plant2 {}}
                      :customer2 {}}
      RECUR-MAP      (recursive-path [] p (cond-path map? (continue-then-stay [MAP-VALS p])))]

    (clojure.test/is (= (setval [RECUR-MAP (pred :age) #(> 30 (:age %))] NONE input)
                        desired-output)))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...