I have a function which outputs a list of maps which look something like this:
({:size20160721 "19.94 GB", :size20160720 "19.94 GB", :size20160719 "100 GB", :directory_path "/user/1 "}
{:size20160721 "1 GB", :size20160720 "4 GB", :size20160719 "10 GB", :directory_path "/user/2 "}
...
)
how can I reorder this list of maps so that each map has :directory_path first, followed by :size keys the number of which which can be variable but will be the same for all paths?
use sorted-map-by, with comparator which would always set:directory_path on the first place:
user>
(def data '({:size20160721 "19.94 GB", :size20160720 "19.94 GB",
:size20160719 "100 GB", :directory_path "/user/1 "}
{:size20160721 "1 GB", :size20160720 "4 GB",
:size20160719 "10 GB", :directory_path "/user/2 "}))
;; #'user/data
user>
(def dirfirst (sorted-map-by #(cond (= :directory_path %1) -1
(= :directory_path %2) 1
:else (compare %1 %2))))
;; #'user/dirfirst
user> (map (partial into dirfirst) data)
;; ({:directory_path "/user/1 ", :size20160719 "100 GB",
;; :size20160720 "19.94 GB", :size20160721 "19.94 GB"}
;; {:directory_path "/user/2 ", :size20160719 "10 GB",
;; :size20160720 "4 GB", :size20160721 "1 GB"})
Related
I am new to clojure.
I have a map s1 and a vector s2
(def s1 {:params [{:param :begin_date
:name "begin date"
:dtk :date
:param_operand :single}
{:param :begin_num
:name "begin num"
:dtk :numeric
:param_operand :single}
{:param :begin_num2
:name "begin num2"
:dtk :numeric
:biparam_operand :single}]})
(def s2 [:begin_date :begin_num])
I am trying to find the match from s1 with s2. I.E iterate through each element from s2 and find the match from s1.
If the match exists check the type for :dtk, if the type is :date then we accept that map.
This is the code which I have tried, when I had to find if the match exists using just one element from the list s2 - :begin_date
(defn test->map
[s1 s2]
(let [test-vec (->> s1
:params
(filter (fn [typet] (= (:param typet) :begin_date)))
first
:dtk)]
(when (= test-vec :date)
"done")))
How to modify the above function in a way that it should be able to iterate over the entire list of elements and find a match.
TIA.
One way to do it using keep:
(defn match [k]
(->> s1
:params
(keep
(fn [{:keys [param] :as v}]
(when (= param k)
v)))
first))
or by using for with :when:
(defn lookup [k]
(first
(for [{:keys [param] :as v} (:params s1)
:when (= param k)]
v)))
(This is bit similar to your approach with for and filter. Only :when does the filtering now, and I return the actual value instead of the string "done".)
With result:
(map lookup s2)
;; => ({:param :begin_date, :name "begin date", :dtk :date, :param_operand :single}
;; {:param :begin_num, :name "begin num", :dtk :numeric, :param_operand :single})
I would personally transform the current data structure s1 to a Clojure map (for example via reduce) once, so you can access the params by key and use select-keys. Then you don't have to iterate over the whole of s2 for every lookup.
(defn transform [s1]
(reduce
(fn [acc {:keys [param] :as v}]
(assoc acc param v))
{}
(:params s1)))
(transform s1)
;; => {:begin_date
;; {:param :begin_date, :name "begin date", :dtk :date, :param_operand :single},
;; :begin_num
;; {:param :begin_num, :name "begin num", :dtk :numeric, :param_operand :single},
;; :begin_num2
;; {:param :begin_num2,
;; :name "begin num2",
;; :dtk :numeric,
;; :biparam_operand :single}}
(select-keys (transform s1) s2)
;; => {:begin_date
;; {:param :begin_date, :name "begin date", :dtk :date, :param_operand :single},
;; :begin_num
;; {:param :begin_num, :name "begin num", :dtk :numeric, :param_operand :single}}
normally when you print a map, the values are unquoted.
(print {:abc "0" :def "1"}) results in {:abc 0 :def 1}. I would like the output to look like {:abc "0" :def "1"}
I attempted to use the map function to get at every key-value pair and that did not work.
This was my attempt:
(defn print-map [m]
(print "{")
(map #((print (first %) "\"" (second %) "\",")) m)
(print "}\n")
)
nothing from the map gets printed
just use pr/prn instead of print/println, since they generate the string that could be read back by reader, meaning the strings would be quoted:
user=> (prn {:a "10" :b 20 :c "21"})
{:a "10", :b 20, :c "21"}
nil
(print (str {:a "82834"}))
;{:a "82834"}
=> nil
Assume I have this
(def base ["one" "two" "three"])
I want to convert it to:
1. one
2. two
3. three
(aka 1. one \n2. two \n3. three)
with join, I am not sure I can append a counter before joining:
(clojure.string/join " \n" base)
=> "one \ntwo \nthree"
and with doseq or similar, plus an atom, I do get individual strings but then will have to concatenate later on, something like
(def base ["one" "two" "three"])
(def pos (atom 0))
(defn add-pos
[base]
(for [b base]
(do
(swap! pos inc)
(str #pos ". " b))))
(let [pos-base (add-pos base)]
(clojure.string/join " \n" pos-base))
=> "1. one \n2. two \n3. three"
While it works, I don't know if using an atom with a for statement is he best way to do this, it doesn't look very clojure-esque.
Is there a better way to do this please?
That's a job for keep-indexed:
user> (keep-indexed #(str (inc %1) ". " %2) ["one" "two" "three"])
("1. one" "2. two" "3. three")
user> (clojure.string/join "\n"
(keep-indexed
#(str (inc %1) ". " %2)
["one" "two" "three"]))
"1. one\n2. two\n3. three"
A minor alternative to schaueho's keep-indexed would be map-indexed (spotting a pattern?)
(def base ["one" "two" "three"])
(defn numbered-list [s]
(->> s
(map-indexed #(str (inc %1) ". " %2))
(interpose \newline)
(apply str)))
(numbered-list base) ; => "1. one\n2. two\n3. three"
Clearly a job for interleave.
(->> (interleave (rest (range)) (repeat ". ") base (repeat " \n"))
(apply str))
;-> "1. one \n2. two \n3. three \n"
I'm using clj-pdf to generate pdf files. As the README file suggests, the library provides some rudimentary templating options.
For example, given a vector of maps, such as:
(def employees
[{:country "Germany",
:place "Nuremberg",
:occupation "Engineer",
:name "Neil Chetty"}
{:country "Germany",
:place "Ulm",
:occupation "Engineer",
:name "Vera Ellison"}])
and a template
(def employee-template
(template
[:paragraph
[:heading $name]
[:chunk {:style :bold} "occupation: "] $occupation "\n"
[:chunk {:style :bold} "place: "] $place "\n"
[:chunk {:style :bold} "country: "] $country
[:spacer]]))
The following output will be produced:
(employee-template employees)
([:paragraph [:heading "Neil Chetty"]
[:chunk {:style :bold} "occupation: "] "Engineer" "\n"
[:chunk {:style :bold} "place: "] "Nuremberg" "\n"
[:chunk {:style :bold} "country: "] "Germany" [:spacer]]
[:paragraph [:heading "Vera Ellison"]
[:chunk {:style :bold} "occupation: "] "Engineer" "\n"
[:chunk {:style :bold} "place: "] "Ulm" "\n"
[:chunk {:style :bold} "country: "] "Germany" [:spacer]])
However, I'm wondering how to use this template in pdf function. When I use
(pdf
[[:heading "Heading 1"]
[:table
{:width 100 :border false :cell-border false :widths [30 70] :offset 35}
[[:cell {:align :right}
(employee-template employees)]
[:cell "dummy"]]]
[:heading "Heading 2"]]
"output.pdf")
I got a invalid tag exception.
If I change (employee-template employees) to (first (employee-template employees)), it works however not as I expect. What is the correct way to use a template?
This works. Generate a new cell for each employee.
(pdf
[[:heading "Heading 1"]
[:table {:width 100 :border false :cell-border false :widths [30 70] :offset 35}
(for [e (employee-template employees)]
[:cell {:align :right} e])]
[:heading "Heading 2"]]
"output.pdf")
Use apply and concat maybe?
(pdf
(apply concat
[[:heading "Heading 1"]]
(employee-template employees)
[[:heading "Heading 2"]])
"output.pdf")
I think there should be a better choice to do this,the concat may not be efficient.
I am writing a piece of code that needs to read in a text file that has data. The text file is in the format:
name 1 4
name 2 4 5
name 3 1 9
I am trying to create a vector of a map in the form [:name Sarah :weight 1 cost :4].
When I try reading the file in with the line-seq reader, it reads each line as an item so the partition is not correct. See repl below:
(let [file-text (line-seq (reader "C://Drugs/myproject/src/myproject/data.txt"))
new-test-items (vec (map #(apply struct item %) (partition 3 file-text)))]
(println file-text)
(println new-test-items))
(sarah 1 1 jason 4 5 nila 3 2 jonas 5 6 judy 8 15 denny 9 14 lis 2 2 )
[{:name sarah 1 1, :weight jason 4 5, :value nila 3 2 } {:name jonas 5 6, :weight judy 8 15, :value denny 9 14}]
I then tried to just take 1 partition, but still the structure is not right.
=> (let [file-text (line-seq (reader "C://Drugs/myproject/src/myproject/data.txt"))
new-test-items (vec (map #(apply struct item %) (partition 1 file-text)))]
(println file-text)
(println new-test-items))
(sarah 1 1 jason 4 5 nila 3 2 jonas 5 6 judy 8 15 denny 9 14 lis 2 2 )
[{:name sarah 1 1, :weight nil, :value nil} {:name jason 4 5, :weight nil, :value nil} {:name nila 3 2 , :weight nil, :value nil} {:name jonas 5 6, :weight nil, :value nil} {:name judy 8 15, :weight nil, :value nil} {:name denny 9 14, :weight nil, :value nil} {:name lis 2 2, :weight nil, :value nil} {:name , :weight nil, :value nil}]
nil
Next I tried to slurp the file, but that is worse:
=> (let [slurp-input (slurp "C://Drugs/myproject/src/myproject/data.txt")
part-items (partition 3 slurp-input)
mapping (vec (map #(apply struct item %) part-items))]
(println slurp-input)
(println part-items)
(println mapping))
sarah 1 1
jason 4 5
nila 3 2
jonas 5 6
judy 8 15
denny 9 14
lis 2 2
((s a r) (a h ) (1 1) (
Please help! This seems like such an easy thing to do in Java, but is killing me in Clojure.
split it into a sequence of lines:
(line-seq (reader "/tmp/data"))
split each of them into a sequence of words
(map #(split % #" ") data)
make a function that takes a vector of one data and turns it into a map with the correct keys
(fn [[name weight cost]]
(hash-map :name name
:weight (Integer/parseInt weight)
:cost (Integer/parseInt cost)))
then nest them back together
(map (fn [[name weight cost]]
(hash-map :name name
:weight (Integer/parseInt weight)
:cost (Integer/parseInt cost)))
(map #(split % #" ") (line-seq (reader "/tmp/data"))))
({:weight 1, :name "name", :cost 4}
{:weight 2, :name "name", :cost 4}
{:weight 3, :name "name", :cost 1})
you can also make this more compact by using zip-map
You are trying to do everything in one place without testing intermediate results. Instead Clojure recommends to decompose task into a number of subtasks - this makes code much more flexible and testable. Here's the code for your task (I assume records in file describe people):
(defn read-lines [filename]
(with-open [rdr (clojure.java.io/reader filename)]
(doall (line-seq rdr))))
(defn make-person [s]
(reduce conj (map hash-map [:name :weight :value] (.split s " "))))
(map make-person (read-lines "/path/to/file"))