2010-09-12 37 views
3

这是我的输入数据:如何将矢量映射到地图,重复键值?

[[: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]]} 

回答

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

关于你的代码中,最显著的问题是命名。首先,我不会,尤其是没有第一次了解您的代码,就不知道annotation,gffseqname是什么意思。 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 []) 
() 

在Clojure中,与其他lisps不同,空列表是truthy。

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

+0

感谢您的评论,它有很大的帮助。 – 2010-09-12 17:30:04

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,它可以逐个解决问题而不是理解更大的代码并找出问题。

4

虽然我已经在你的代码没有评论,我想这对我自己和与此解决方案提出了:

(defn build-annotations [coll] 
    (let [anmap (group-by first coll)] 
    (zipmap (keys anmap) (map #(vec (map (comp vec rest) %)) (vals anmap))))) 
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))) 

取决于你真正想要的东西,你甚至可以用只做组由度日。

+0

'group-by'完全符合我的需要,实际上。谢谢! – 2010-09-12 17:31:20

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

所以,

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

需要[键&值]的集合,并返回{键[值]}的列表

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

需要的地图列表,合并它们在一起,并且如果密钥已经存在则连接值。

+0

谢谢,这实际上是我迄今为止首选的解决方案。我喜欢它的简洁,你的解释非常清楚。 – 2010-09-14 01:33:18