#clojure #clojurescript
Вопрос:
Допустим, у меня есть коллекция, подобная:
(def xs
[{:name "Apple" :type "Fruit is a type"}
{:name "Tomato" :type "Vegetable are food"}
{:name "Pear" :type "the type can also be Fruit"}
{:name "Steak" :type "eat less Meat"}])
И я хочу отфильтровать и сгруппировать по коллекции во что-то вроде этого:
{:Fruit [{:name "Apple" :type "Fruit is a type"} {:name "Pear" :type "the type can also be Fruit"}] :Vegetable [{:name "Tomato" :type "Vegetable are food"}]
В настоящее время я просто фильтрую результаты, но, похоже, не могу найти хороший способ группирования. Вот что у меня есть до сих пор:
(defn filter-response [x query]
(filter #(s/includes? (:type %) query) x))
(defn group-by-types [queries]
(map #(filter-response xs %) queries))
(group-by-types ["Fruit" "Vegetable"])
Как я могу этого добиться?
Ответ №1:
Обновленный Ответ
Вы можете использовать понимание списка, чтобы проверить каждый элемент в коллекции для каждого шаблона.
(defn- all-occurrences [xs patterns]
(for [x xs
pattern patterns
:when (clojure.string/includes? (:type x) pattern)]
[(keyword pattern) x]))
Или с помощью вашей filter-response
функции:
(defn- all-occurrences [xs patterns]
(for [pattern patterns
x (filter-response xs pattern)]
[(keyword pattern) x]))
Затем используйте сокращение с обновлением, чтобы объединить список вхождений в единую карту:
(defn group-by-patterns [xs patterns]
(reduce (fn [m [pattern text]] (update m pattern conj text))
{}
(all-occurrences xs patterns)))
Вызов его с новым вводом:
(def xs
[{:name "Apple" :type "Fruit is a type"}
{:name "Tomato" :type "Vegetable are food"}
{:name "Pear" :type "the type can also be Fruit"}
{:name "Steak" :type "eat less Meat"}])
(group-by-patterns xs ["Fruit" "Vegetable"])
=> {:Fruit ({:name "Pear", :type "the type can also be Fruit"} {:name "Apple", :type "Fruit is a type"}),
:Vegetable ({:name "Tomato", :type "Vegetable are food"})}
Оригинальный Ответ
Сначала вы можете использовать группировку по, чтобы сгруппировать значения по указанным ключам:
(def xs
[{:name "Apple" :type "Fruit"}
{:name "Tomato" :type "Vegetable"}
{:name "Pear" :type "Fruit"}
{:name "Steak" :type "Meat"}])
erdos=> (group-by :type xs)
{"Fruit" [{:name "Apple", :type "Fruit"} {:name "Pear", :type "Fruit"}],
"Vegetable" [{:name "Tomato", :type "Vegetable"}],
"Meat" [{:name "Steak", :type "Meat"}]}
Затем используйте клавиши выбора для фильтрации ключей:
erdos=> (select-keys (group-by :type xs) ["Fruit" "Vegetable"])
{"Fruit" [{:name "Apple", :type "Fruit"} {:name "Pear", :type "Fruit"}],
"Vegetable" [{:name "Tomato", :type "Vegetable"}]}
Если вам нужны ключи ключевых слов, вам нужен дополнительный шаг сопоставления:
erdos=> (into {}
(for [[k v] (select-keys (group-by :type xs) ["Fruit" "Vegetable"])]
[(keyword k) v]))
{:Fruit [{:name "Apple", :type "Fruit"} {:name "Pear", :type "Fruit"}],
:Vegetable [{:name "Tomato", :type "Vegetable"}]}
Комментарии:
1. Оооо, это хороший ответ.
2. Я понимаю, что упустил очень важную деталь: поиск
:type
ключа основан на сопоставлении строк. Мои фактические данные выглядят так:{:type "Fruit is a type"}, {:type "The type can also be Fruit}
и т. Д. Вот почему у меня есть clojure.string/включает. Я постараюсь включить это в решение.3. о, в этом есть смысл. Я обновил свой ответ, чтобы отразить изменения.
4. Еще раз спасибо! Отличные ответы