2010-03-14 43 views
7

我想創建一個表格(工作日程表)我以前使用Python編碼,我認爲這將是一個很好的介紹給我的Clojure語言。從嵌套的地圖(和向量)創建一個HTML表格

我在Clojure(或者lisp)方面的經驗很少,而且我在谷歌和一些嘗試和錯誤方面做了很多工作,但似乎無法擺脫這種編碼風格。

這裏是我的樣本數據(從SQLite數據庫是未來的未來):

(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"]}]})) 

我試圖逐步完成這個原本使用,然後移動到doseq終於domap(它似乎更成功),並將內容轉儲到一個html表格中(我的原始python程序使用COM將sqlite數據庫輸出到excel電子表格中)。

這裏是我的嘗試(在創建表FN):

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

,與部分(職員,經理等),並在各部分的名稱輸出表,我只是覺得我」因爲我可能需要添加更多的domaps才能在適當的列中獲得移位時間,並且代碼對它有'骯髒'的感覺,所以通過嵌套它來濫用domap。

如果我沒有提供足夠的信息,我很抱歉,我通常不會尋求編碼方面的幫助,這也是我的第一個問題:)。

如果你知道任何更好的方法來做到這一點,甚至提示或技巧,我應該知道作爲一個新手,他們肯定是受歡迎的。

謝謝。

+0

未來,您不應該標記像這個社區維基這樣的可回答的問題。這破壞了我們的聲譽收集遊戲。 ;-) – 2010-03-15 01:59:29

+0

對不起,我沒有意識到它扔掉了rep系統。我只是認爲這意味着我的問題是可編輯的(它不需要無論如何):(但謝謝你回答不管,我從你的帖子中學到了很多。:) – Kenny164 2010-03-15 16:52:26

+0

很高興聽到這一點。 :-) – 2010-03-15 21:58:48

回答

3

有沒有辦法來避免某種嵌套循環。但是您根本不需要domap,Compojure足夠聰明(有時)爲您擴展seq。 listmapfor就足夠了。例如米哈爾Marczyk的答案,或者:

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

「地圖在一個序列,並在相同的代碼包一切」的模式是足夠的,我想有時使用一個輔助功能,它常見。

Compojure爲您擴展一個級別seq。因此,您可以在list中投入一些東西,讓標籤順序出現在您的HTML輸出中,這樣我就可以讓h1和hr顯示出來。在你的代碼中,你只是把h1和hr扔掉。

但請注意,它只擴展一個級別。當你有一個列表或一系列序列表時,外部序列會擴展,但內部序列不會。

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

看看內部seq如何使Compojure barf。看看compojure.html.gen/expand-seqs,看看它是如何工作的,或者如果你關心的話,改變/修復它。

由於for返回一個惰性seq,因此在list中嵌套forfor時可能會出現問題。但是你必須避免有一個seq-in-a-seq。我使用上面的list*listhtml的組合也可以工作。還有很多其他的方法。

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

哇謝謝,我真的很喜歡map-tag幫助函數的想法,並且你讓我成爲研究列表*(我也不知道它是否存在) – Kenny164 2010-03-15 16:54:50

2

我認爲你的代碼有一些小問題。我試圖在下面的代碼中糾正它們。用Compojure 0.3.2測試這個,我敢說它是有效的。 (隨意點出任何需要改善或似乎並不爲你工作的,當然。)

(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

嗯,實際上喜歡div的使用,這是一個很好的方式來包含嵌套循環(這是我從第一個地方移動的主要原因,因爲有太多的爭論抱怨)。謝謝 – Kenny164 2010-03-15 17:06:39

+0

Ouch,Brian的帖子讓我意識到我沒有在create-table中用'html'形式包裝':h1',':hr'和':table',所以我也一直在扔掉它們。 ..會在一秒鐘內修復。至於':div',我認爲它也可以,實際上是爲了最清晰的代碼,儘管有一些'concat' /'list *'等等,原則上你可以不用它。 – 2010-03-15 21:50:37

+0

我認爲HTML標準不允許在div中包裝表格行或單元格。但我可能是錯的。 – 2010-03-16 00:22:25