特定のマップのキーと値を交換する機能はありますか?したがって、マップが与えられた場合、キーを値にし、キーを評価します。
(swap {:a 2 b 4}) => {2 :a 4 :b}
それを行う1つの方法は
(zipmap (vals my-map) (keys my-map))
しかし、clojureがこれにユーティリティfnを提供するかどうか疑問に思っていますか?
特定のマップのキーと値を交換する機能はありますか?したがって、マップが与えられた場合、キーを値にし、キーを評価します。
(swap {:a 2 b 4}) => {2 :a 4 :b}
それを行う1つの方法は
(zipmap (vals my-map) (keys my-map))
しかし、clojureがこれにユーティリティfnを提供するかどうか疑問に思っていますか?
map-invert
これがinの目的ですclojure.set
:
user=> (clojure.set/map-invert {:a 2 :b 4})
{4 :b, 2 :a}
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)))
後日これを読む人にとっては、次のことが役立つはずだと思います。
小さなライブラリはここから入手できますhttps://clojars.org/beoliver/map-inversions
マップを反転すると、リレーションが返される場合があります。マップが全単射(1対1)の場合、逆も1対1になります。マップが(多くの場合のように)多対1である場合は、セットまたはベクトルを使用する必要があります。
#アトミックとして扱われる値##1対1マップの値は一意です
(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}
##多対1マップの値は一意ではありません。これは非常に一般的です-そしてあなたの地図がこの形であると仮定するのが最も安全です...だから(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
#コレクションとして扱われる値## 1対多の値はセット/コレクションですが、それらの共通部分は常に空です。(2つの異なるセットで要素は発生しません)
(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
##多対多の値はセット/コレクションであり、共通部分が空でない値が少なくとも2つ存在します。値がコレクションである場合は、それらがこのカテゴリに分類されると想定するのが最善です。
(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
。
これは、reduceを使用して問題に適合する可能性のあるオプションです。
(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})