Dimas Angga Saputra
Dimas Angga Saputra

Reputation: 211

Accessing value from defn

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?

Upvotes: 0

Views: 100

Answers (4)

kazuwal
kazuwal

Reputation: 1091

If you want to use def in this instance:

(def user-map-other
  (fn [new-name new-phone new-email]
    {:name new-name
     :phone new-phone
     :email new-email}))

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]"

Upvotes: 0

Thumbnail
Thumbnail

Reputation: 13473

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.

Upvotes: 0

T.Gounelle
T.Gounelle

Reputation: 6033

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"

Upvotes: 0

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91554

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.

Upvotes: 1

Related Questions