Менять ключи и значения на карте - программирование

Менять ключи и значения на карте

Есть ли функция для замены ключа и значения данной карты. Поэтому, учитывая карту, я хочу, чтобы ключи стали значениями, и значения клавиш.

(swap {:a 2 b 4}) => {2 :a 4 :b}

Один из способов сделать это -

(zipmap (vals my-map) (keys my-map))

Однако интересно ли, что clojure предоставляет для этого утилиту fn?

4b9b3361

Ответ 1

Это цель map-invert в clojure.set:

user=> (clojure.set/map-invert {:a 2 :b 4})
{4 :b, 2 :a}

Ответ 2

Там функция reverse-map в clojure.contrib.datalog.util реализована как:

(defn reverse-map
  "Reverse the keys/values of a map"
  [m]
  (into {} (map (fn [[k v]] [v k]) m)))

Ответ 3

Для тех, кто читает это позже, я думаю, что следующее должно быть полезно.

Инвертирование карты может вернуть отношение. Если карта является инъективной (взаимно-однозначной), то обратная также будет взаимно однозначной. Если карта (как бы часто бывает) многозначна, то вы должны использовать набор или вектор.

Значения, обрабатываемые как атомные

один-к-одному

значения карты являются уникальными

(defn invert-one-to-one
  "returns a one-to-one mapping"
  [m]
  (persistent! (reduce (fn [m [k v]] (assoc! m v k)) (transient {}) m)))

(def one-to-one {:a 1 :b 2 :c 3})

> (invert-one-to-one one-to-one)
{1 :a 2 :b 3 :c}

много-к-одному

Значения карты не уникальны. Это очень распространено - и безопаснее предположить, что ваши карты имеют такую ​​форму... так (def invert invert-many-to-one)

(defn invert-many-to-one
  "returns a one-to-many mapping"
  ([m] (invert-many-to-one #{} m))
  ([to m]
   (persistent!
    (reduce (fn [m [k v]]
              (assoc! m v (conj (get m v to) k)))
            (transient {}) m))))

(def many-to-one {:a 1 :b 1 :c 2})

> (invert-many-to-one many-to-one)
{1 #{:b :a}, 2 #{:c}} ; as expected

> (invert-many-to-one [] many-to-one)
{1 [:b :a], 2 [:c]} ; we can also use vectors

> (invert-one-to-one many-to-one) ; what happens when we use the 'wrong' function?
{1 :b, 2 :c} ; we have lost information

Значения, обрабатываемые как коллекции

один-ко-многим

значения являются наборами/наборами, но их пересечения всегда пусты. (Ни один элемент не встречается в двух разных наборах)

(defn invert-one-to-many
  "returns a many-to-one mapping"
  [m]
  (persistent!
   (reduce (fn [m [k vs]] (reduce (fn [m v] (assoc! m v k)) m vs))
           (transient {}) m)))

(def one-to-many (invert-many-to-one many-to-one))
> one-to-many
{1 #{:b :a}, 2 #{:c}}

> (invert-one-to-many one-to-many)
{:b 1, :a 1, :c 2} ; notice that we don't need to return sets as vals

многие-ко-многим

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

(defn invert-many-to-many
  "returns a many-to-many mapping"
  ([m] (invert-many-to-many #{} m))
  ([to m]
   (persistent!
    (reduce (fn [m [k vs]]
              (reduce (fn [m v] (assoc! m v (conj (get m v to) k))) m vs))
            (transient {}) m))))

(def many-to-many {:a #{1 2} :b #{1 3} :c #{3 4}})

> (invert-many-to-many many-to-many)
{1 #{:b :a}, 2 #{:a}, 3 #{:c :b}, 4 #{:c}}

;; notice that there are no duplicates when we use a vector
;; this is because each key appears only once
> (invert-many-to-many [] many-to-many)
{1 [:a :b], 2 [:a], 3 [:b :c], 4 [:c]}

> (invert-many-to-one many-to-many)
{#{1 2} #{:a}, #{1 3} #{:b}, #{4 3} #{:c}}

> (invert-one-to-many many-to-many)
{1 :b, 2 :a, 3 :c, 4 :c}

> (invert-one-to-one many-to-many)
{#{1 2} :a, #{1 3} :b, #{4 3} :c} ; this would be missing information if we had another key :d mapping to say #{1 2}

Вы также можете использовать invert-many-to-many в примере one-to-many.

Ответ 4

Вот вариант, который может сместиться с помощью сокращения:

(reduce #(assoc %1 (second %2) (first %2)) {} {:a 2 :b 4})

Здесь в функции

(defn invert [map]
  (reduce #(assoc %1 (second %2) (first %2)) {} map))

Вызов

(invert {:a 2 b: 4})

Тогда есть reduce-kv (чище, на мой взгляд)

(reduce-kv #(assoc %1 %3 %2) {} {:a 2 :b 4})