代码之家  ›  专栏  ›  技术社区  ›  Pedro Silva

如何将向量映射到一个映射,将重复的键值推入其中?

  •  3
  • Pedro Silva  · 技术社区  · 14 年前

    这是我的输入数据:

    [[: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-with ( conj ),但在撞了我的头一段时间后,我准备找人来帮我。

    解决方案 (无论如何,离得够近了):

    (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 回复  |  直到 14 年前
        1
  •  9
  •   MayDaniel    14 年前
    (defn build-annotations [coll]
      (reduce (fn [m [k & vs]]
                (assoc m k (conj (m k []) (vec vs))))
              {} coll))
    

    annotation , gff seqname . current remaining 通常称为 more

    在你的陈述中, 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中,与其他lisp不同,空列表是真实的。

    This

        2
  •  4
  •   ponzao    14 年前

    (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,一段一段地解决问题比理解更大的代码并从中发现问题更容易。

        3
  •  4
  •   Thomas Wagner    14 年前

    虽然我还没有对您的代码发表任何评论,但我自己也尝试过,并提出了以下解决方案:

    (defn build-annotations [coll]
      (let [anmap (group-by first coll)]
        (zipmap (keys anmap) (map #(vec (map (comp vec rest) %)) (vals anmap)))))
    
        4
  •  2
  •   Alex Miller    14 年前

    下面是我的条目,虽然这里的几个步骤实际上是关于返回向量而不是列表。如果你放弃了这个要求,它会变得简单一些:

    (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)))
    

    取决于你真正想要什么,你甚至可以只做小组讨论。

        5
  •  2
  •   wilkes    14 年前
    (defn build-annotations [coll]
      (apply merge-with concat 
             (map (fn [[k & vals]] {k [vals]}) 
                  coll))
    

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

    获取[keys&values]的集合并返回{key[values]}的列表

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