Fork me on GitHub

Anything can be a key in a map but typically you use keywords as they resolve to their own identity everytime and they are useful as they implement IFn to look themselves up in a map.


Hi, I'm looking for a function that is equivalent to PHP's compact() or ES6's “object literal property value shorthand”, which take a list of variables/symbols that exist in the current scope and return a map with the names and values of each one.


Here's what I came up with:

(defmacro compact
  [& args]
  (let [keys (map keyword args)
        vals `(~@args)]
    (zipmap keys vals) ))

; Example
(let [artist "The Beatles"
      track "Let It Be"]
  (compact artist track))
; => {:artist "The Beatles", :track "Let It Be"}


Surely there has to be a built-in function or a more idiomatic way to do this with Clojure?


You don’t need the macro. You can just do (zipmap (map keyword [keyword-vec]) [vals-vec]).


Or you can use reduce-kv.

Chris O’Donnell15:02:12

@raphaelsaunier: That looks like a reasonable solution to me, though you could avoid using a macro by passing in symbols and using eval. For the record, here's my version of your macro: (defmacro compact [& args] (into {} (map (juxt keyword identity) args))).


Ah, juxt. Another one I always forget about.


@akiva, right, but is there a way to generate keyword-vec programatically? I only ended up using a macro because with functions I could only get the values of the symbols but not their names…

Chris O’Donnell15:02:08

I think it would be better to implement it as a function as a function, like (defn compact [& args] (into {} (map (juxt keyword eval) args))). Then you'd call it like (let [a 1 b 2] (compact 'a 'b)).


@codonnell: nice! Hadn't thought of that! simple_smile

Chris O’Donnell15:02:36

As a function it's clearer you're working with the symbols, rather than their values.


using eval like that is rarely a good idea


Having said that, is there a more idiomatic way of solving this? I'm using this in a function that defines a couple of bindings and does a few computations within let and then returns a map.


@raphaelsaunier: in clojure we rarely use those kind of "magic" macros that rely on locals names


@raphaelsaunier, I think @codonnell is on the right track here.


I wouldn’t try to use the binding symbols themselves but have that information as part of the val.

Chris O’Donnell15:02:17

@raphaelsaunier: It's hard to say without more context. If there are too many variables to explicitly write the map, you should probably have them already grouped in a data structure which tells you what's what.


Thanks! Yeah sorry, I realised that my last question was a bit too vague after hitting enter. I'm not returning that many variables (<5), but it's just a convenience function that you get used to very quickly when writing ES6.

Chris O’Donnell16:02:34

That's how I would write your function, I think.

Chris O’Donnell16:02:45

But maybe with slightly more correct parens.


Thanks! Yes obviously, but I get the gist!


Guys, totally a newbie question How can i edit a date object? (i want to add to hours)


@olegakbarov: Take a look at it works really well for that


alright — i just needed to parse the string 😅 (t/plus (f/parse iso-8601 2016-02-06T12:45:00) (t/hours 2))