0
votes

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?

4

4 Answers

1
votes

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" "[email protected]")

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.

0
votes

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" "[email protected]"))
(def you  (map->user {:email "[email protected]" :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"
0
votes

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" "[email protected]")
;{:email "[email protected]", :phone "123456789", :name "me"}

If performance is pressing, by all means use records instead of maps, as @AbbéRésina suggests.

0
votes

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" "[email protected]") :phone) => "999"
(get (user-map-other "Ben" "999" "[email protected]") :name) => "Ben"
(get (user-map-other "Ben" "999" "[email protected]") :email) => "[email protected]"

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" "[email protected]") :phone) => "999"
(get (user-map "Ben" "999" "[email protected]") :name) => "Ben"
(get (user-map "Ben" "999" "[email protected]") :email) => "[email protected]"