Я хочу преобразовать одну карту значений в другую карту с теми же ключами, но с функцией, примененной к значениям. Я бы подумал, что в api clojure есть функция для этого, но мне не удалось ее найти.
Вот пример реализации того, что я ищу
(defn map-function-on-map-vals [m f]
(reduce (fn [altered-map [k v]] (assoc altered-map k (f v))) {} m))
(println (map-function-on-map-vals {:a "test" :b "testing"} #(.toUpperCase %)))
{:b TESTING, :a TEST}
Кто-нибудь знает, map-function-on-map-vals
существует ли уже? Я думаю, да (возможно, и с более красивым названием).
map
почему-то ожидал [фм] как в .Вы можете использовать
clojure.algo.generic.functor/fmap
:user=> (use '[clojure.algo.generic.functor :only (fmap)]) nil user=> (fmap inc {:a 1 :b 3 :c 5}) {:a 2, :b 4, :c 6}
источник
org.clojure/algo.generic "0.1.0"
теперь в примере должно быть написано:(use '[clojure.algo.generic.functor :only (fmap)]) (fmap inc {:a 1 :b 3 :c 5})
fmap
в ClojureScript?Вот довольно типичный способ трансформации карты.
zipmap
берет список ключей и список значений и "поступает правильно", создавая новую карту Clojure. Вы также можете положитьmap
вокруг клавиш, чтобы изменить их, или и то, и другое.(zipmap (keys data) (map #(do-stuff %) (vals data)))
или обернуть его своей функцией:
(defn map-function-on-map-vals [m f] (zipmap (keys m) (map f (vals m))))
источник
Взято из Поваренной книги Clojure, есть сокращение-kv:
(defn map-kv [m f] (reduce-kv #(assoc %1 %2 (f %3)) {} m))
источник
Вот довольно идиоматический способ сделать это:
(defn map-function-on-map-vals [m f] (apply merge (map (fn [[k v]] {k (f v)}) m)))
Пример:
user> (map-function-on-map-vals {1 1, 2 2, 3 3} inc)) {3 4, 2 3, 1 2}
источник
map-map
,map-map-keys
Иmap-map-values
Я не знаю никакой существующей функции в Clojure для этого, но вот реализация этой функции,
map-map-values
которую вы можете скопировать. Он поставляется с двумя тесно связанными функциямиmap-map
иmap-map-keys
, которые также отсутствуют в стандартной библиотеке:(defn map-map "Returns a new map with each key-value pair in `m` transformed by `f`. `f` takes the arguments `[key value]` and should return a value castable to a map entry, such as `{transformed-key transformed-value}`." [f m] (into (empty m) (map #(apply f %) m)) ) (defn map-map-keys [f m] (map-map (fn [key value] {(f key) value}) m) ) (defn map-map-values [f m] (map-map (fn [key value] {key (f value)}) m) )
Применение
Вы можете позвонить
map-map-values
так:(map-map-values str {:a 1 :b 2}) ;; => {:a "1", :b "2"}
И две другие функции вроде этого:
(map-map-keys str {:a 1 :b 2}) ;; => {":a" 1, ":b" 2} (map-map (fn [k v] {v k}) {:a 1 :b 2}) ;; => {1 :a, 2 :b}
Альтернативные реализации
Если вы хотите
map-map-keys
илиmap-map-values
без более общейmap-map
функции, вы можете использовать эти реализации, которые не полагаются наmap-map
:(defn map-map-keys [f m] (into (empty m) (for [[key value] m] {(f key) value} ))) (defn map-map-values [f m] (into (empty m) (for [[key value] m] {key (f value)} )))
Кроме того , вот альтернативная реализация
map-map
, которая основана наclojure.walk/walk
аinto
, если вы предпочитаете эту формулировку:(defn map-map [f m] (clojure.walk/walk #(apply f %) identity m) )
Версии Parellel -
pmap-map
и др.Также существуют параллельные версии этих функций, если они вам нужны. Они просто используют
pmap
вместоmap
.(defn pmap-map [f m] (into (empty m) (pmap #(apply f %) m)) ) (defn pmap-map-keys [f m] (pmap-map (fn [key value] {(f key) value}) m) ) (defn pmap-map-values [f m] (pmap-map (fn [key value] {key (f value)}) m) )
источник
Я - Clojure n00b, поэтому могут быть более элегантные решения. Вот мой:
(def example {:a 1 :b 2 :c 3 :d 4}) (def func #(* % %)) (prn example) (defn remap [m f] (apply hash-map (mapcat #(list % (f (% m))) (keys m)))) (prn (remap example func))
Функция anon создает небольшой список из двух ключей для каждого ключа и его значения. Mapcat запускает эту функцию над последовательностью ключей карты и объединяет все работы в один большой список. "apply hash-map" создает новую карту из этой последовательности. (% M) может выглядеть немного странно, это идиоматический Clojure для применения ключа к карте для поиска связанного значения.
Настоятельно рекомендуется прочитать: Шпаргалка по Clojure .
источник
(defn map-vals "Map f over every value of m. Returns a map with the same keys as m, where each of its values is now the result of applying f to them one by one. f is a function of one arg, which will be called which each value of m, and should return the new value. Faster then map-vals-transient on small maps (8 elements and under)" [f m] (reduce-kv (fn [m k v] (assoc m k (f v))) {} m)) (defn map-vals-transient "Map f over every value of m. Returns a map with the same keys as m, where each of its values is now the result of applying f to them one by one. f is a function of one arg, which will be called which each value of m, and should return the new value. Faster then map-vals on big maps (9 elements or more)" [f m] (persistent! (reduce-kv (fn [m k v] (assoc! m k (f v))) (transient {}) m)))
источник
Мне нравится твоя
reduce
версия. С очень небольшими вариациями, он также может сохранять тип структуры записей:(defn map-function-on-map-vals [m f] (reduce (fn [altered-map [k v]] (assoc altered-map k (f v))) m m))
Файл
{}
был заменен наm
. После этого изменения записи остаются записями:(defrecord Person [firstname lastname]) (def p (map->Person {})) (class p) '=> Person (class (map-function-on-map-vals p (fn [v] (str v)))) '=> Person
Начиная с
{}
, запись теряет свою возможность записи , которую можно было бы сохранить, если вам нужны возможности записи (например, компактное представление памяти).источник
Я задаюсь вопросом , почему никто не упомянул призрак библиотеки еще. Он был написан для того, чтобы упростить кодирование такого рода преобразований (и, что еще важнее, легко понять написанный код), при этом сохраняя при этом высокую производительность:
(require '[com.rpl.specter :as specter]) (defn map-vals [m f] (specter/transform [specter/ALL specter/LAST] f m)) (map-vals {:a "test" :b "testing"} #(.toUpperCase %))
Написать такую функцию на чистом Clojure просто, но код становится намного сложнее, когда вы переходите к сильно вложенному коду, состоящему из различных структур данных. И здесь появляется Spectre .
Я рекомендую посмотреть этот выпуск на Clojure TV, который объясняет мотивацию и детали Spectre .
источник
Clojure 1.7 ( выпущенный 30 июня 2015 г.) предоставляет для этого элегантное решение
update
:(defn map-function-on-map-vals [m f] (map #(update % 1 f) m)) (map-function-on-map-vals {:a "test" :b "testing"} #(.toUpperCase %)) ;; => ([:a "TEST"] [:b "TESTING"])
источник