2014-02-21 1 views
2

У меня есть HTML представлены в странной форме (это гораздо проще работать, чем обычная гнездовые один):алгоритм для преобразования из одного HTML представления других

  [{:text "5d" :em true :strong true} 
      {:text "xx" :em true} 
      {:text "damn" :em true :strong true} 
      {:text "c6"} 
      {:text "qwe" :em true} 
      {:text "asd"} 
      {:text "qqq" :em true :strong true}] 

Мне нужно, чтобы преобразовать его к икота, как один:

  [[:em 
      [:strong "5d"] 
      "xx" 
      [:strong "damn"]] 
      "c6" 
      [:em "qwe"] 
      "asd" 
      [:strong [:em "qqq"]]] 

лучшее внедрение я придумал это:

(defn wrap-tags [states nodes] 
    (if (seq states) 
    (reduce 
    (fn [nodes state] 
     [(into [state] nodes)]) 
    nodes states) 
    nodes)) 

(defn p->tags 
    ([data] 
    (p->tags data #{} [] [])) 
    ([[node & rest] state waiting result] 
    (let [new-state (set (keys (dissoc node :text))) 
      closed (clojure.set/difference state new-state) 
      waiting (conj (wrap-tags closed waiting) (:text node)) 
      result (if-not (seq new-state) 
        (into result waiting) 
        result) 
      waiting (if-not (seq new-state) [] waiting)] 
     (if (seq rest) 
     (p->tags rest new-state waiting result) 
     (if (seq waiting) 
      (into result (wrap-tags new-state waiting)) 
      result))))) 

Он не работает должным образом, хотя он не обрабатывает случай, когда появляется сильное (он не знает, сколько «ожидающих» узлов он должен обернуть и обернуть их все), но у меня нет идей, как отслеживать это). Это выглядит немного уродливо для меня, но это менее раздражает. :) Что он возвращает для моего дела прямо сейчас:

[[:em 
    [:strong 
    [:strong "5d"] 
    "xx" 
    "damn"]] 
"c6" 
[:em "qwe"] 
"asd" 
[:em [:strong "qqq"]]] 

Мне бы хотелось услышать какие-либо идеи по улучшению моего кода.

+1

Кажется, вам нужна обратная сторона алгоритма сглаживания дерева, поскольку данные, которые у вас есть, являются результатом сплющивания дерева икоты. – Ankur

+0

Да, что-то в этом роде, я просто не мог придумать хороший способ сделать это. –

+1

почему первый '{: text" 5d ": em true: strong true}' приводит к '[: em [: strong]]', last one '{: text" qqq ": em true: strong true} => [ : strong [: em]] ' – edbond

ответ

0

Хорошо, это, кажется, я выиграл эту игру:

(defn node->tags [node] 
    (set (keys (dissoc node :text)))) 

(defn tag-reach [data tag] 
    (reduce (fn [cnt node] 
      (if (tag node) 
       (inc cnt) 
       (reduced cnt))) 
      0 data)) 

(defn furthest-tag [data exclude] 
    (let [exclude (into #{:text} exclude) 
     tags (filterv #(not (exclude %)) (node->tags (first data)))] 
    (if (seq tags) 
     (reduce (fn [[tag cnt :as current] rival] 
       (let [rival-cnt (tag-reach data rival)] 
        (if (> rival-cnt cnt) 
        [rival rival-cnt] 
        current))) 
       [nil 0] tags) 
     [nil 1]))) 

(defn nodes->tree 
    ([nodes] 
    (nodes->tree nodes [])) 
    ([nodes wrapping-tags] 
    (loop [nodes nodes 
      result []] 
     (let [[tag cnt] (furthest-tag nodes wrapping-tags) 
      [to-process to-recur] (split-at cnt nodes) 
      processed (if tag 
         (nodes->tree to-process (conj wrapping-tags tag)) 
         (mapv :text to-process)) 
      result (into result (if tag 
            [(into [tag] processed)] 
            processed))] 
     (if (seq to-recur) 
      (recur to-recur result) 
      result))))) 

(deftest test-gen-tree 
    (let [data [{:text "5d" :em true :strong true} 
       {:text "xx" :em true} 
       {:text "qqq" :em true :strong true} 
       {:text "c6"} 
       {:text "qwe" :em true} 
       {:text "asd"} 
       {:text "qqq" :em true :strong true}]] 
    (is (= (nodes->tree data) 
      [[:em 
      [:strong "5d"] 
      "xx" 
      [:strong "qqq"]] 
      "c6" 
      [:em "qwe"] 
      "asd" 
      [:strong [:em "qqq"]]])))) 

Это не так ясно, как хотелось бы, но это работает. Ура. :-)

2

Если я правильно понимаю расположение ваших данных, похоже, что вы хотите разделить последовательность на то, содержат или нет элементы :em, и если они это сделают, у них есть те, которые завернуты в сторону одного узла [:em...]. Clojure-х partition-by можно использовать, чтобы сделать это:

(def elements [{:text "5d" :em true :strong true}                    
       {:text "xx" :em true}                       
       {:text "damn" :em true :strong true}                    
       {:text "c6"}                          
       {:text "qwe" :em true}                       
       {:text "asd"}                         
       {:text "qqq" :em true :strong true}]) 

(vec (partition-by #(:em %1) elements))   
;; =>                    
[({:text "5d", :strong true, :em true} 
    {:text "xx", :em true} 
    {:text "damn", :strong true, :em true})       
({:text "c6"})                            
({:text "qwe", :em true})                         
({:text "asd"})                            
({:text "qqq", :strong true, :em true})] 

Затем можно обработать это с reduce создать икота, как структура:

(defn group->tag [acc group]                         
    (cond                               
    (nil? group)                            
    acc                               

    (:em (first group))                           
    (conj                              
    acc                              
    (vec                              
     (concat [:em]                            
       (mapv                            
       (fn [elt]                          
       (if (contains? elt :strong)                     
        [:strong (:text elt)]                      
        (:text elt)))                        
       group))))                          

    :otherwise                             
    (vec (concat acc (mapv :text group)))))                      

(defn elements->hiccup [elements]                        
    (reduce                              
    group->tag                             
    []                               
    (partition-by #(:em %1) elements))) 

Вышеприведенные выглядит, как он производит то, что вы просили:

(elements->hiccup elements)                          
;; =>                               
[[:em                               
    [:strong "5d"]                            
    "xx"                               
    [:strong "damn"]]                            
"c6"                               
[:em "qwe"]                             
"asd"                               
[:em [:strong "qqq"]]] 
+1

Это делает немного смысла, но дело в том, что это может быть наоборот, сильные оборачивает метки и эм оборачивает меньше теги. Другими словами, более или менее свободный HTML-код. Поэтому мне нужно определить, какой тег должен быть внешней оболочкой, внутри которой находится и т. Д. Как сказал первый комментатор, в основном сглаживание дерева HTML. –

Смежные вопросы