I am new at Clojure. I have a map like
{:title "The Little Schemer"
:authors [friedman , felleisen]}
I want to transform it to:
{:title "The Little Schemer"
:authors #{friedman , felleisen}}
I attempted like:
(def friedman {:name "Daniel Friedman" :birth-year 1944})
(def felleisen {:name "Matthias Felleisen"})
(defn old-book->new-book [book]
(set (:authors book)
)
)
(println (old-book->new-book {:title "The Little Schemer"
:authors [friedman , felleisen]}))
; => Output: #{{:name Daniel Friedman, :birth-year 1944} {:name Matthias Felleisen}}
; => Expected-Output: #{friedman , felleisen}
Here the defs friedman and felleisen gets executed and there results are getting transformed to set. But, I want the function names to be converted to set instead of their results.
First of all try to println this:
(println {:title "The Little Schemer"
:authors [friedman , felleisen]})
The output will be:
{:title The Little Schemer, :authors [{:name Daniel Friedman, :birth-year 1944} {:name Matthias Felleisen}]}
So, what happened here? As You know in this context friedman and felleisen is a variables, so, if You print them - they will be appear in a print message by values. For example:
(def a 1)
(println a)
Will print 1, because of a is just a variable.
The code which you shown here do what You want and when you print it - then values of variable of friedman and felleisen substitute by values.
Your vector of autors((:authors [friedman felleisen])) after disposing the function set will be converted to a set, what we saw from Your output.
Related
This question already has an answer here:
Clojure printing lazy sequence
(1 answer)
Closed 7 years ago.
LazySeq is kicking my butt when I try to log its value.
(require '[clojure.tools.logging :as log])
(def layer->multipart [{:name "layer-name" :content "veg"} {:name "layer-tag" :content "abs"}])
(def field->multipart [{:name "field-id" :content "12345"} {:name "field-version" :content "v1"}])
(log/infof "concat is %s" (concat layer->multipart field->multipart))
; => 2016-02-16 16:31:11,707 level=INFO [nREPL-worker-38] user:288 - concat is clojure.lang.LazySeq#87177bed
; WTF is clojure.lang.LazySeq#87177bed?
I've check the How to convert lazy sequence to non-lazy in Clojure answer and it suggests that all I need to do is doall and all my dreams will come true. But alas...no.
(log/infof "concat is %s" (doall (concat layer->multipart field->multipart)))
; => 2016-02-16 16:31:59,958 level=INFO [nREPL-worker-40] user:288 - concat is clojure.lang.LazySeq#87177bed
; still clojure.lang.LazySeq#87177bed is not what I wanted
I've observed that (pr-str (concat layer->multipart field->multipart)) does what I want, but it makes no sense; The docs for pr-str say something about "pr to a string" and the docs for pr say "Prints the object(s) to the output stream that is the current value of *out*.". I don't want anything going to *out*, I just want the string value returned so the logger can use it!
(log/infof "concat is %s" (pr-str (concat layer->multipart field->multipart)))
; => 2016-02-16 16:42:02,927 level=INFO [nREPL-worker-1] user:288 - concat is ({:content "veg", :name "layer-name"} {:content "abs", :name "layer-tag"} {:content "12345", :name "field-id"} {:content "v1", :name "field-version"})
; this is what I wanted but I don't want anything going to *out*...or do I?
What do I have to do to get the effect of the pr-str variant without worrying about anything inadvertently getting dumped to stdout (I'm guessing that is what *out* is)? I want the lazy sequence to be fully realized for logging (it never gets too big...it only ends up as lazy as an accident of concat).
How can I log the full value of my LazySeq?
The problem is that behind the scenes the logger is calling .toString on your lazy sequence. Try this:
user=> (.toString (concat layer->multipart field->multipart))
;; "clojure.lang.LazySeq#87177bed"`
What you really want is to convert the contents of the sequence into a string. For example:
(log/infof "concat is %s" (apply str (concat layer->multipart field->multipart)))
;; Feb 16, 2016 5:10:19 PM clojure.tools.logging$eval420$fn__424 invoke
;; INFO: concat is {:name "layer-name", :content "veg"}{:name "layer-tag", :content "abs"}{:name "field-id", :content "12345"}{:name "field-version", :content "v1"}
By the way, pr-str is fine too. As it name says it prints to a string, not to *out*. You're using that string.
I am very new to closure so I am not fully sure how to do this. If I have a file data.txt with the following:
[
{:name "Steve"}
{:name "Issac"}
{:name "Lucas"}
{...}
]
I want to be able to read the contents of each :name tag and do something with the return value (in this case it will be printing to the console). I looked up online and found there is a method called reader and I understand how to open a file.
The Closure syntax confuses me slightly so I am not sure how to do this.
there should be 2 possiblities:
1) raw clojure by means clojure.core/read-string
(read-string "['q 2 \"test\"]")
;; [(quote q) 2 "test"]
2) via clojure.edn/read-string
(clojure.edn/read-string "['q 2 \"test\"]")
;; ['q 2 "test"]
the 2nd one should be faster and safer (but does not eval and stuff),
but is only for edn format (this is a subset of clojure code)
the string dummy (i.e from your data.txt)
;; a string, just for demo
(def s "[{:name \"Steve\" email: \"foo#bar.com\" }
{:name \"Issac\"}
{:name \"Lucas\"}]")
the rest is plain clojure, if you have trouble with clojure maps here is the doc
(doseq [name (map :name (clojure.edn/read-string s))]
(println name))
;; Steve
;; Issac
;; Lucas
;; nil
I need advice,
I try to make function :
(def user-map [new-name new-phone new-email]
{:name new-name
:phone new-phone
:email new-email})
With new-name, new-phone, new-email are user input. But when i try to compile it, it says too many arguments to def, after change def to defn, when i try to execute user-map in REPL i get something like
#<temp_atom$user_address zenedu.mm.dbase.temp_atom$user_address#714924b5
instead of actual map.
I need to get to the map, any advice?
It sounds like perhaps you are conceptually combining the value that will be returned from calling user-map as a function with some arguments and evaluating the symbol user-map on its own.
Evaluating
(user-map "me" "123456789" "me#here.com")
Which will return a map, by looking up the var user-map in the current namespace and calling the function stored in that var with these arguments. Where evaluating just
user-map
Will simply look up the var user-map in the current namespace and return the contents of that var, which in the case where you used defn, will be the function it's self. The REPL then prints the object I'd of that function.
In your use case, you need defn to define a builder (like a constructor in Java) for the object you want. The log
#<temp_atom$user_address zenedu.mm.dbase.temp_atom$user_address#714924b5
suggests that you are using another structure user-address somewhere in the application and it looks like there is confusion between a user-map object and this user-address.
Anyway, you may be interested to have a look at defrecord that provides a convenient way to build objects with a constructor (and potentially other functions related to this object), e.g.
(defrecord user [name phone email])
defrecord provides 2 constructors ->user and map->user:
(def me (->user "abb" "0102030405" "abb#mail.com"))
(def you (map->user {:email "das#mail.com" :phone "9090909090" :name "das"}))
And you can access the properties of a user through keywords exactly like a map:
user> (:name me)
"abb"
user> (:phone you)
"9090909090"
OK, you should use defn instead of def.
But what information really varies here? The number and order of the map keys, in this case [:name :phone :email].
A generic function that will build - from a key sequence - a function that will build the map from the value sequence is
(defn map-builder [keys]
(fn [& values] (zipmap keys values)))
You can then define
(def user-map (map-builder [:name :phone :email]))
... which works as required:
(user-map "me" "123456789" "me#here.com")
;{:email "me#here.com", :phone "123456789", :name "me"}
If performance is pressing, by all means use records instead of maps, as #AbbéRésina suggests.
Putting it simply...
The error you are receiving is due to essentially passing a vector as a second value to def. If you want to use def in this instance go with...
(def user-map-other
(fn [new-name new-phone new-email]
{:name new-name
:phone new-phone
:email new-email}))
Here we are using an anonymous function that accepts your three parameters. Here is a link to learn more about them => http://clojuredocs.org/clojure.core/fn
To gain access to the values contained in your function we can use get in this instance.
(get (user-map-other "Ben" "999" "bbb#mail.com") :phone) => "999"
(get (user-map-other "Ben" "999" "bbb#mail.com") :name) => "Ben"
(get (user-map-other "Ben" "999" "bbb#mail.com") :email) => "bbb#mail.com"
A more concise method would be to use defn as represented below.
(defn user-map [new-name new-phone new-email]
{:name new-name
:phone new-phone
:email new-email})
(get (user-map "Ben" "999" "bbb#mail.com") :phone) => "999"
(get (user-map "Ben" "999" "bbb#mail.com") :name) => "Ben"
(get (user-map "Ben" "999" "bbb#mail.com") :email) => "bbb#mail.com"
I've got two databases that I'm attempting to keep in sync using a bit of Clojure glue code.
I'd like to make something like a clojure.set/difference that operates on values projected by a function.
Here's some sample data:
(diff #{{:name "bob smith" :favourite-colour "blue"}
{:name "geraldine smith" :age 29}}
#{{:first-name "bob" :last-name "smith" :favourite-colour "blue"}}
:name
(fn [x] (str (:first-name x) " " (:last-name x))))
;; => {:name "geraldine smith" :age 29}
The best I've got is:
(defn diff
"Return members of l who do not exist in r, based on applying function
fl to left and fr to right"
[l r fl fr]
(let [l-project (into #{} (map fl l))
r-project (into #{} (map fr r))
d (set/difference l-project r-project)
i (group-by fl l)]
(map (comp first i) d)))
But I feel that this is a bit unwieldly, and I can't imagine it performs very well. I'm throwing away information that I'd like to keep, and then looking it up again.
I did have a go using metadata, to keep the original values around during the set difference, but I can't seem put metadata on primitive types, so that didn't work...
I'm not sure why, but I have this tiny voice inside my head telling me that this kind of operation on the side is what monads are for, and that I should really get around to finding out what a monad is and how to use it. Any guidance as to whether the tiny voice is right is very welcome!
(defn diff
[l r fl fr]
(let [r-project (into #{} (map fr r))]
(set (remove #(contains? r-project (fl %)) l))))
This no longer exposes the difference operation directly (it is now implicit with the remove / contains combination), but it is succinct and should give the result you are looking for.
example usage and output:
user> (diff #{{:name "bob smith" :favourite-colour "blue"}
{:name "geraldine smith" :age 29}}
#{{:first-name "bob" :last-name "smith" :favourite-colour "blue"}}
:name
(fn [x] (str (:first-name x) " " (:last-name x))))
#{{:age 29, :name "geraldine smith"}}
A still-learning clojure-newbie (me) got a list of maps.
Each map contains one account number and other information
(e.g. ({:account 123, :type "PK", :end "01.01.2013", ...} {:account 456 :type "GK" :end "01.07.2016", ...})
Now I need a function that sequentially puts a increasing number and the account number
( like {1, 123, 2, 456 etc}). And I didn't get it no matter what I tried.
I once learned Delphi, and it would be there like
for i :=1 to (count MYMAP)
do (put-in-a-list i AND i-th account number in the list)
inc i
Due to some restrictions I am not allowed to use functions out of the core and also I must not use "use", "ns", "require", "cycle", "time", "loop", "while", "defn", "defstruct", "defmacro", "def", "defn", "doall", "dorun", "eval", "read-string", "repeatedly", "repeat", "iterate", "import", "slurp", "spit" .
And - please excuse me if there is any bad english - it's not usual for me to ask such questions in english.
For lazy sequence of natural numbers interspersed with account numbers, you can try something like the following:
(interleave ; splices together the following sequences
(map inc (range)) ; an infinite sequence of numbers starting at 1
(map :account ; gets account numbers out of maps
[{:account 123, :type "PK", :end "01.01.2013", ...}, ...])) ; your accounts
However, the {} notation in your example ({1, 123, 2, 456 etc}) suggests you might be more interested in a map. In that case, you can use zipmap:
(zipmap ; makes a map with keys from first sequence to values from the second
(map inc (range))
(map :account
[{:account 123, :type "PK", :end "01.01.2013", ...}, ...]))
map-indexed will help you create an increasing number sequence:
user> (let [f (comp (partial into {})
(partial map-indexed #(vector (inc %) (:account %2))))]
(f [{:account 123, :type "PK", :end "01.01.2013"} {:account 456 :type "GK" :end "01.07.2016"}]))
{1 123, 2 456}