如何将矢量映射到地图,将重复键值推入其中?

时间:2010-09-12 09:56:38

标签: vector clojure hashmap

这是我的输入数据:

[[:a 1 2] [:a 3 4] [:a 5 6] [:b \a \b] [:b \c \d] [:b \e \f]]

我想将此映射到以下内容:

{:a [[1 2] [3 4] [5 6]] :b [[\a \b] [\c \d] [\e \f]]}

这是我到目前为止所做的:

(defn- build-annotation-map [annotation & m]
 (let [gff (first annotation)
       remaining (rest annotation)
       seqname (first gff)
       current {seqname [(nth gff 3) (nth gff 4)]}]
   (if (not (seq remaining))
     m
     (let [new-m (merge-maps current m)]
       (apply build-annotation-map remaining new-m)))))

(defn- merge-maps [m & ms]
  (apply merge-with conj
         (when (first ms)                                                                                                              
           (reduce conj                     ;this is to avoid [1 2 [3 4 ... etc.                                                                                                          
                   (map (fn [k] {k []}) (keys m))))                                                                                    
         m ms))

以上产生:

{:a [[1 2] [[3 4] [5 6]]] :b [[\a \b] [[\c \d] [\e \f]]]}

我似乎很清楚问题出在merge-maps,特别是传递给merge-withconj)的函数,但是在我敲了一会儿之后,我'我准备有人帮助我。

我一般都是lisp的新手,特别是clojure,所以我也非常感谢那些没有专门解决这个问题的评论,还有我的风格,脑死亡等等。谢谢!

解决方案(足够接近):

(group-by first [[:a 1 2] [:a 3 4] [:a 5 6] [:b \a \b] [:b \c \d] [:b \e \f]])
=> {:a [[:a 1 2] [:a 3 4] [:a 5 6]], :b [[:b \a \b] [:b \c \d] [:b \e \f]]}

5 个答案:

答案 0 :(得分:9)

(defn build-annotations [coll]
  (reduce (fn [m [k & vs]]
            (assoc m k (conj (m k []) (vec vs))))
          {} coll))

关于您的代码,最重要的问题是命名。首先,我不会,尤其是在没有先了解您的代码的情况下,不知道annotationgffseqname的含义。 current也很模糊。在Clojure中,remaining通常称为more,具体取决于上下文,以及是否应使用更具体的名称。

在你的let语句gff (first annotation) remaining (rest annotation)中,我可能会利用解构,如下所示:

(let [[first & more] annotation] ...)

如果您更愿意使用(rest annotation),那么我建议您使用next,因为如果它是空的,它将返回nil,并允许您编写(if-not remaining ...)而不是(if-not (seq remaining) ...)user> (next []) nil user> (rest []) ()

{{1}}

在Clojure中,与其他lisps不同,空列表是真实的。

This文章显示了惯用命名的标准。

答案 1 :(得分:4)

至少在给定的数据集上起作用。

(defn build-annotations [coll]
  (reduce
    (fn [result vec]
      (let [key (first vec)
            val (subvec vec 1)
            old-val (get result key [])
            conjoined-val (conj old-val val)]
        (assoc
          result
          key
          conjoined-val)))
    {}
    coll))

(build-annotations [[:a 1 2] [:a 3 4] [:a 5 6] [:b \a \b] [:b \c \d] [:b \e \f]])

我很抱歉没有对您的代码进行改进。我只是在学习Clojure,它更容易一块一块地解决问题,而不是理解更大的代码并找到问题。

答案 2 :(得分:4)

虽然我对你的代码没有任何评论,但我自己尝试了这个并提出了这个解决方案:

(defn build-annotations [coll]
  (let [anmap (group-by first coll)]
    (zipmap (keys anmap) (map #(vec (map (comp vec rest) %)) (vals anmap)))))

答案 3 :(得分:2)

这是我的分组利用分组,虽然这里的几个步骤真的关心的是返回向量而不是列表。如果你放弃了这个要求,它会变得更简单:

(defn f [s]
  (let [g (group-by first s)
        k (keys g)
        v (vals g)
        cleaned-v (for [group v]
                    (into [] (map (comp #(into [] %) rest) group)))]
    (zipmap k cleaned-v)))

根据您的实际需要,您甚至可以通过分组来实现目标。

答案 4 :(得分:2)

(defn build-annotations [coll]
  (apply merge-with concat 
         (map (fn [[k & vals]] {k [vals]}) 
              coll))

所以,

(map (fn [[k & vals]] {k [vals]}) 
     coll))

收集了[keys& amp;值]并返回{key [values]}

列表
(apply merge-with concat ...list of maps...)

获取地图列表,将它们合并在一起,并在密钥已存在的情况下对值进行连接。