Как мне сопоставить вектор с картой, вставив в нее повторяющиеся ключевые значения? - PullRequest
3 голосов
/ 12 сентября 2010

Это мои входные данные:

[[: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), но после стука моегоПодожди немного, я уже готов к тому, что кто-то мне поможет.

Я новичок в 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 ]

9 голосов
/ 12 сентября 2010
(defn build-annotations [coll]
  (reduce (fn [m [k & vs]]
            (assoc m k (conj (m k []) (vec vs))))
          {} coll))

Что касается вашего кода, наиболее значимой проблемой является наименование.Во-первых, я бы не стал, особенно без предварительного понимания вашего кода, иметь представление о том, что подразумевается под annotation, gff и seqname.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, в отличие от других списков, пустой список правдив.

Это статья показывает стандарт для идиоматических имен.

4 голосов
/ 12 сентября 2010

Хотя у меня пока нет комментариев к вашему коду, я попробовал его для себя и придумал это решение:

(defn build-annotations [coll]
  (let [anmap (group-by first coll)]
    (zipmap (keys anmap) (map #(vec (map (comp vec rest) %)) (vals anmap)))))
4 голосов
/ 12 сентября 2010

Работает, по крайней мере, на заданном наборе данных.

(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 голосов
/ 14 сентября 2010
(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...)

принимает списоксопоставляет, объединяет их и объединяет значения, если ключ уже существует.

2 голосов
/ 12 сентября 2010

Вот моя запись, использующая группирование, хотя несколько шагов здесь действительно касаются возврата векторов, а не списков. Если вы отбросите это требование, оно станет немного проще:

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

В зависимости от того, что вы на самом деле хотите, вы можете даже обойтись, просто группируя.

...