Pergunta

Quero transformar um mapa de valores em outro mapa com as mesmas teclas, mas com uma função aplicada aos valores. Eu acho que havia uma função para fazer isso na API Clojure, mas não consegui encontrá -la.

Aqui está um exemplo de implementação do que estou procurando

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

Alguém sabe se map-function-on-map-vals já existe? Eu pensaria que aconteceu (provavelmente com um nome melhor também).

Foi útil?

Solução

eu gosto do seu reduce versão muito bem. Eu acho que é idiomático. Aqui está uma versão usando a compreensão da lista de qualquer maneira.

(defn foo [m f]
  (into {} (for [[k v] m] [k (f v)])))

Outras dicas

Você pode usar o 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}

Aqui está uma maneira bastante típica de transformar um mapa. zipmap pega uma lista de chaves e uma lista de valores e "faz a coisa certa" produzindo um novo mapa de clojure. Você também pode colocar o map Em torno das chaves para alterá -las, ou ambas.

(zipmap (keys data) (map #(do-stuff %) (vals data)))

ou para encerrá -lo em sua função:

(defn map-function-on-map-vals [m f]
    (zipmap (keys m) (map f (vals m))))

Retirado do livro de receitas Clojure, há Reduce-KV:

(defn map-kv [m f]
  (reduce-kv #(assoc %1 %2 (f %3)) {} m))

Aqui está uma maneira bastante idiomática de fazer isso:

(defn map-function-on-map-vals [m f]
        (apply merge
               (map (fn [[k v]] {k (f v)})
                    m)))

Exemplo:

user> (map-function-on-map-vals {1 1, 2 2, 3 3} inc))
{3 4, 2 3, 1 2}

map-map, map-map-keys, e map-map-values

Não conheço nenhuma função existente em clojure para isso, mas aqui está uma implementação dessa função como map-map-values que você está livre para copiar. Ele vem com duas funções intimamente relacionadas, map-map e map-map-keys, que também estão ausentes na biblioteca padrão:

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

Uso

Você pode ligar map-map-values assim:

(map-map-values str {:a 1 :b 2})
;;           => {:a "1", :b "2"}

E as outras duas funções como esta:

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

Implementações alternativas

Se você só quiser map-map-keys ou map-map-values, sem o mais geral map-map função, você pode usar essas implementações, que não confiam em 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)} )))

Além disso, aqui está uma implementação alternativa de map-map isso é baseado em clojure.walk/walk ao invés de into, se você preferir esta frase:

(defn map-map [f m]
    (clojure.walk/walk #(apply f %) identity m) )

Versões parelel - pmap-map, etc.

Também existem versões paralelas dessas funções, se você precisar delas. Eles simplesmente usam pmap ao invés de 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) )

Eu sou um Clojure N00B, então pode haver soluções muito mais elegantes. Aqui está o meu:

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

O ANON Func cria uma pequena lista de cada chave e seu valor F'Ed. O MapCat executa essa função sobre a sequência das teclas do mapa e concatena todo o trabalho em uma grande lista. "Aplicar hash-map" cria um novo mapa a partir dessa sequência. O (% m) pode parecer um pouco estranho, é um clojure idiomático para aplicar uma chave a um mapa para procurar o valor associado.

Leitura mais altamente recomendada: o Folha de dicas de clojure .

eu gosto do seu reduce versão. Com uma variação muito pequena, também pode manter o tipo de estrutura de registros:

(defn map-function-on-map-vals [m f]
  (reduce (fn [altered-map [k v]] (assoc altered-map k (f v))) m m))

o {} foi substituído por m. Com essa mudança, os registros permanecem registros:

(defrecord Person [firstname lastname])

(def p (map->Person {}))
(class p) '=> Person

(class (map-function-on-map-vals p
  (fn [v] (str v)))) '=> Person

Começando com {}, o registro perde seu gravação, qual pode querer reter, se você deseja os recursos de registro (representação de memória compacta, por exemplo).

(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)))
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top