2010-03-14 19 views
7

Sto provando a creare un tavolo (un programma di lavoro) che ho precedentemente codificato usando python, penso che sarebbe una bella introduzione al linguaggio Clojure per me.Creare una tabella HTML da mappe nidificate (e vettori)

Ho poca esperienza in Clojure (o lisp in quella materia) e ho fatto il mio giro in google e un bel po 'di prove ed errori ma non riesco a capire come funziona questo stile di codifica.

Ecco il mio dati di esempio (arriveranno da un database SQLite in futuro):

(def smpl2 (ref {"Salaried" 
      [{"John Doe" ["12:00-20:00" nil nil nil "11:00-19:00"]} 
       {"Mary Jane" [nil "12:00-20:00" nil nil nil "11:00-19:00"]}] 
      "Shift Manager" 
      [{"Peter Simpson" ["12:00-20:00" nil nil nil "11:00-19:00"]} 
       {"Joe Jones" [nil "12:00-20:00" nil nil nil "11:00-19:00"]}] 
      "Other" 
      [{"Super Man" ["07:00-16:00" "07:00-16:00" "07:00-16:00" 
         "07:00-16:00" "07:00-16:00"]}]})) 

stavo cercando di passare da questo originariamente utilizzando per poi si spostano su doseq e infine domap (che sembra avere più successo) e scaricare il contenuto in una tabella html (il mio programma python originale l'ha prodotto da un database sqlite in un foglio di calcolo Excel usando COM).

Qui è il mio tentativo (fn create-tabella):

(defn html-doc [title & body] 
    (html (doctype "xhtml/transitional") 
    [:html [:head [:title title]] [:body body]])) 

(defn create-table [] 
    [:h1 "Schedule"] 
    [:hr] 
    [:table (:style "border: 0; width: 90%") 
    [:th "Name"][:th "Mon"][:th "Tue"][:th "Wed"] 
    [:th "Thur"][:th "Fri"][:th "Sat"][:th "Sun"] 
    [:tr 
    (domap [ct @smpl2] 
     [:tr [:td (key ct)] 
     (domap [cl (val ct)] 
      (domap [c cl] 
       [:tr [:td (key c)]]))]) 
    ]]) 

(defroutes tstr 
    (GET "/" ((html-doc "Sample" create-table))) 
    (ANY "*" 404)) 

che emette la tabella con le sezioni (stipendiati, direttore, ecc) ei nomi nelle sezioni, mi sento come I' abusando del domap annidandolo troppe volte poiché probabilmente avrò bisogno di aggiungere più domaps solo per ottenere i tempi di turno nelle loro colonne appropriate e il codice sta diventando un po '"sporco".

Mi scuso in anticipo se non includo informazioni sufficienti, di solito non chiedo aiuto per la codifica, anche questa è la mia prima domanda SO :).

Se conosci approcci migliori per fare questo o anche consigli o suggerimenti che dovrei sapere come novizio, sono sicuramente i benvenuti.

Grazie.

+0

Per il futuro, non si dovrebbero contrassegnare domande rispondenti come questa wiki della comunità. Questo rovina il nostro gioco di raccolta di reputazione. ;-) –

+0

Mi spiace, non mi sono reso conto che ha gettato via il sistema di ripetizioni. Ho pensato che la mia domanda era modificabile (che non doveva essere comunque) :(. Ma grazie per aver risposto a prescindere, ho imparato molto dai tuoi post :) :) – Kenny164

+0

Felice di sentirlo. :-) –

risposta

3

Non c'è modo di evitare una sorta di ciclo nidificato. Ma non è necessario il domap, Compojure è abbastanza intelligente (a volte) per espandere un seq per te. list e map e for sono sufficienti. La risposta di esempio, Michał Marczyk, o:

(defn map-tag [tag xs] 
    (map (fn [x] [tag x]) xs)) 

(defn create-table [] 
    (list 
    [:h1 "Schedule"] 
    [:hr] 
    [:table {:style "border: 0; width: 90%"} 
    [:tr (map-tag :th ["Name" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"])] 
    [:tr (for [[category people] smpl2] 
      (list* [:tr [:td category]] 
        (for [person people 
         [name hours] person] 
        [:tr [:td name] (map-tag :td hours)])))]])) 

La "mappa nel corso di un ss e avvolgere tutto nello stesso tag" modello è sufficiente che mi piace usare una funzione di supporto per esso a volte comune.

Compojure espande un livello di seq per voi. Quindi puoi inserire alcune cose in uno list per ottenere i tag in modo sequenziale nell'output HTML, cosa che ho fatto per far visualizzare h1 e hr. Nel tuo codice, stai solo buttando via l'h1 e l'ora.

Ma si noti che si espande solo un livello. Quando hai una lista di liste, o una lista di seq, il seq esterno si espanderà, ma quelli interni no.

user> (println (html (list [:div "foo"] (for [x [1 2 3]] [:div x])))) 
<div>foo</div>[email protected] 

Vedere come seq interno rende Compojure barf. Guarda su compojure.html.gen/expand-seqs per vedere come funziona, o cambia/correggi se ti interessa.

Questo può essere un problema quando si hanno nidificato for o un for in un list, dal momento che for restituisce un ss pigro. Ma devi solo evitare di avere un seq-in-a-seq. Io uso list* sopra. Funzionerebbe anche una combinazione di list e html. Ci sono molti altri modi.

user> (println (html (list* [:div "foo"] (for [x [1 2 3]] [:div x])))) 
<div>foo</div><div>1</div><div>2</div><div>3</div> 

user> (println (html (list [:div "foo"] (html (for [x [1 2 3]] [:div x]))))) 
<div>foo</div><div>1</div><div>2</div><div>3</div> 
+0

Wow grazie, mi piace molto l'idea della funzione helper del tag map, e mi hai creato un elenco di ricerche * (che non sapevo nemmeno esistesse) – Kenny164

2

Penso che tu abbia qualche piccolo problema con il tuo codice. Ho tentato di correggerli nel codice qui sotto. Provando questo con Compojure 0.3.2, oserei dire che funziona. (Sentitevi liberi di sottolineare tutto ciò che richiede il miglioramento o sembra non funzionare per voi, naturalmente.)

(use 'compojure) ; you'd use a ns form normally 

;;; I'm not using a ref here; this doesn't change much, 
;;; though with a ref/atom/whatever you'd have to take care 
;;; to dereference it once per request so as to generate a consistent 
;;; (though possibly outdated, of course) view of data; 
;;; this doesn't come into play here anyway 
(def smpl2 {"Salaried"  [{"John Doe" ["12:00-20:00" nil nil nil "11:00-19:00"]} 
          {"Mary Jane" [nil "12:00-20:00" nil nil nil "11:00-19:00"]}] 
      "Shift Manager" [{"Peter Simpson" ["12:00-20:00" nil nil nil "11:00-19:00"]} 
          {"Joe Jones" [nil "12:00-20:00" nil nil nil "11:00-19:00"]}] 
      "Other"   [{"Super Man" ["07:00-16:00" "07:00-16:00" "07:00-16:00" 
              "07:00-16:00" "07:00-16:00"]}]}) 

(defn html-doc [title & body] 
    (html (doctype :xhtml-transitional) ; the idiomatic way to insert 
             ; the xtml/transitional doctype 
     [:html 
     [:head [:title title]] 
     [:body body]])) 

(defn create-table [] 
    (html 
    [:h1 "Schedule"] 
    [:hr] 
    [:table {:style "border: 0; width: 90%;"} 
    [:tr 
    [:th "Name"][:th "Mon"][:th "Tue"][:th "Wed"] 
    [:th "Thur"][:th "Fri"][:th "Sat"][:th "Sun"]] 
    (for [category smpl2] 
     [:div [:tr [:td (key category)]] ; for returns just one thing per 
             ; 'iteration', so I'm using a div 
             ; to package two things together; 
             ; it could be avoided, so tell me 
             ; if it's a problem 
     (for [people (val category)] 
     (for [person people] 
      [:tr 
      [:td (key person)] 
      (for [hours (val person)] 
       [:td hours])]))])])) 

(defn index-html [request] 
    (html-doc "Sample" (create-table))) 

(defroutes test-routes 
    (GET "/" index-html) 
    (ANY "*" 404)) 

(defserver test-server 
    {:port 8080} 
    "/*" 
    (servlet test-routes)) 
+0

hmm, mi è piaciuto l'uso del div in realtà, è un bel modo per contenere i loop nidificati (questo è il motivo principale per cui mi sono trasferito da for al 1 ° posto, perché mi sono lamentato di avere troppi argomenti). Grazie – Kenny164

+0

Ouch, il post di Brian mi ha fatto capire che non sono riuscito a racchiudere ': h1',': hr' e ': table' in un formato' html' in create-table, quindi li ho buttati anche via. .. risolverà in un secondo Per quanto riguarda ': div', ho pensato che fosse ok anche per il codice più chiaro, anche se con alcuni' concat's/'list *' s ecc. In linea di principio si potrebbe farne a meno. –

+0

Penso che le righe della tabella wrapping o le celle in div non siano consentite dallo standard HTML. Potrei sbagliarmi però. –

Problemi correlati