在Clojure里面翻出哈希图

时间:2011-09-02 21:29:52

标签: clojure

我是Clojure的新手,并为你的Clojure大师提出了一个有趣的问题。我正在编写“编程集体智能”一书,并尝试在Clojure中编写示例代码(本书将它们全部用Python编写)。在第一章中,我们有一个电影评论家的哈希映射设置和他们给不同电影的排名。它看起来像这样:

{"Lisa Rose" {"Lady in the Water" 2.5, "Snakes on a Plane" 3.5 },
 "Gene Seymour" {"Lady in the Water" 3.0, "Snakes on a Plane" 3.5}}

问题是这个。如何将其内部转出,以便我得到一个如下所示的哈希映射:

{"Lady in the Water" {"Lisa Rose" 2.5, "Gene Seymour" 3.0},
 "Snakes on a Plane" {"Lisa Rose" 3.5, "Gene Seymour" 3.5}}

你的功能是什么?

3 个答案:

答案 0 :(得分:17)

(let [m {"Lisa Rose" {"Lady in the Water" 2.5, "Snakes on a Plane" 3.5 },
         "Gene Seymour" {"Lady in the Water" 3.0, "Snakes on a Plane" 3.5}}]
  (apply merge-with merge
         (for [[ok ov] m
               [ik iv] ov]
           {ik {ok iv}})))

{"Snakes on a Plane" {"Gene Seymour" 3.5, "Lisa Rose" 3.5},
 "Lady in the Water" {"Gene Seymour" 3.0, "Lisa Rose" 2.5}}

答案 1 :(得分:6)

(defn inverse-map [m]
  (let [inner-keys (-> m first val keys)
        outer-keys (keys m)]
    (apply merge-with merge
           (for [ik inner-keys
                 ok outer-keys]
             {ik {ok (get-in input [ok ik])}}))))

这假设内部地图中的所有感兴趣的关键字都出现在第一个内部地图上。如果这不正确,则(-> m first val keys)必须替换为返回所有感兴趣的密钥集合的内容,例如(->> m (map values) (mapcat keys))

我们的想法是构建{inner-key {outer-key the-value-at-inner-key-in-the-map-at-outer-key}}形式的地图,然后将它们合适地合并在一起。

问题文本中地图上的返回值是指定的。

现在,上面创建了许多中间结构,这可能是性能方面的问题。如果速度至关重要,您可以切换到loop和瞬态:

(defn transient-inverse-map [m]
  (let [inner-keys (-> m first val keys)
        outer-keys (keys m)
        t (transient {})]
    (loop [inner-keys inner-keys
           t t]
      (if (seq inner-keys)
        (recur (next inner-keys)
               (assoc! t ik
                       (let [ik (first inner-keys)
                             t  (transient {})]
                         (loop [outer-keys outer-keys
                                t t]
                           (if (seq outer-keys)
                             (let [ok (first outer-keys)]
                               (recur (next outer-keys)
                                      (assoc! t ok (get-in m [ok ik]))))
                             (persistent! t))))))
        (persistent! t)))))

如果难以辨别代码,那么这个想法是一样的。

答案 2 :(得分:3)

我可以建议如下: 我们有地图 - 条目集合。将每个条目翻出来并将它们合并: 初始:

{:Lisa {:Lady 2.5, :Snakes 3.5},
 :Gene {:Lady 3.0, :Snakes 3.5}}

反转每个条目:

([:Lady {:Lisa 2.5}], [:Snakes {:Lisa 3.5}])
([:Lady {:Gene 3.0}], [:Snakes {:Gene 3.5}])

Concat他们:

([:Lady {:Lisa 2.5}], [:Snakes {:Lisa 3.5}], [:Lady {:Gene 3.0}], [:Snakes {:Gene 3.5}])

他们将它们合并到一张地图上:

{:Lady {:Lisa 2.5, :Gene 3.0},
 :Snakes {:Lisa 3.5, :Gene 3.5}}

代码:

(defn inverse-map [m]
        (let [inverse-entry (fn [[name movies]]
                                (map (fn [[movie rating]]
                                         [movie {name rating}])
                                     movies))]
           (->> (map inverse-entry m)
                (reduce concat)
                (reduce (fn [res [movie entry]]
                            (update-in res [movie] merge entry))
                        {}))))

所以我们得到map,(它是向量[key value]的集合),反向每个条目(vector [key value])。现在我们收集了向量集合,将它们连接到一个集合。最后,使用reduce我们添加每个矢量来映射。

我想有更优雅的解决方案,但我的也有效。