swap keys and values in a map
是否具有交换给定映射的键和值的功能。 因此,给定一个映射,我希望键成为值,并重视键。
1 | (swap {:a 2 b 4}) => {2 :a 4 :b} |
一种方法是
1 | (zipmap (vals my-map) (keys my-map)) |
但是想知道clojure是否为此提供了实用程序fn?
这是
1 2 | user=> (clojure.set/map-invert {:a 2 :b 4}) {4 :b, 2 :a} |
clojure.contrib.datalog.util中有一个函数
1 2 3 4 | (defn reverse-map "Reverse the keys/values of a map" [m] (into {} (map (fn [[k v]] [v k]) m))) |
对于以后阅读此书的任何人,我认为以下内容将有所帮助。
反转地图可能会返回关系。如果映射是内射的(一对一),则逆也将是一对一的。如果地图(通常如此)是多对一的,则应使用集合或向量。
视为原子的值
一对一
地图的值是唯一的
1 2 3 4 5 6 7 8 9 | (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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | (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 3 4 5 6 7 8 9 10 11 12 13 | (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 |
多对多
值是集合/集合,并且至少存在两个交集不为空的值。如果您的值是集合,则最好假定它们属于此类别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | (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} |
您还可以在
这是一个使用reduce可能适合该问题的选项:
1 | (reduce #(assoc %1 (second %2) (first %2)) {} {:a 2 :b 4}) |
这里有一个功能
1 2 | (defn invert [map] (reduce #(assoc %1 (second %2) (first %2)) {} map)) |
呼唤
1 | (invert {:a 2 b: 4}) |
然后是
1 | (reduce-kv #(assoc %1 %3 %2) {} {:a 2 :b 4}) |