Why are there so many map building features in clojure?

Beginner question, but I really don’t understand why there are so many operations for building maps in clojure.

Do you have conj , assoc and merge , but they seem to more or less do the same thing?

 (assoc {:a 1 :b 2} :c 3) (conj {:a 1 :b 2} {:c 3}) (merge {:a 1 :b 2} {:c 3}) 

What is the difference and why are all these methods necessary when they do more or less the same thing?

+43
clojure
Jul 08 '10 at 12:41
source share
3 answers

assoc and conj behave differently for other data structures:

 user=> (assoc [1 2 3 4] 1 5) [1 5 3 4] user=> (conj [1 2 3 4] 1 5) [1 2 3 4 1 5] 

If you are writing a function that can handle several kinds of collections, then your choice will go a long way.

Consider merge as a function only for maps (it is similar to conj for other collections).

My opinion:

  • assoc - use when you change existing key / value pairs.
  • conj - use when you add new key / value pairs.
  • merge - use when you combine two or more cards
+47
Jul 08 '10 at 14:01
source share

In fact, these functions behave differently when used with cards.

  • conj :

    Firstly, the example (conj {:a 1 :b 2} :c 3) from the question text does not work at all (neither with 1.1 nor with 1.2; IllegalArgumentException not thrown). There are only a few types that can be conj to maps, namely two-element vectors, clojure.lang.MapEntry (which are basically equivalent to two-element vectors) and mappings.

    Note that map seq contains a bunch of MapEntry s. So you can do, for example,

     (into a-map (filter a-predicate another-map)) 

    (note that into uses conj - or conj! when possible - internally). Neither merge nor assoc allows you to do this.

  • merge :

    This is almost exactly equivalent to conj , but replaces its nil arguments with {} - empty hash maps - and thus returns the map when the first "map" in the chain is nil .

     (apply conj [nil {:a 1} {:b 2}]) ; => ({:b 2} {:a 1}) ; clojure.lang.PersistentList (apply merge [nil {:a 1} {:b 2}]) ; => {:a 1 :b 2} ; clojure.lang.PersistentArrayMap 

    Check there (except docstring ...) to stop the programmer from using merge with other types of collections. If someone does this, strangeness arises; Not recommended.

  • assoc :

    Again, an example from the question text - (assoc {:a 1 :b 2} {:c 3}) will not work; instead, it will throw an IllegalArgumentException . assoc accepts a card argument followed by an even number of arguments - in odd positions (let's say the card is in position 0) are keys, and in even ones are values. I find that I assoc things on cards more often than I conj , although when I conj , assoc will seem cumbersome .; -)

  • merge-with :

    For completeness, this is the last basic feature regarding maps. This is very useful to me. It works as the pointer indicates. here is an example:

     (merge-with + {:a 1} {:a 3} {:a 5}) ; => {:a 9} 

    Please note that if the card contains a “new” key that did not occur on any of the cards to the left of it, the merge function will not be called. This is sometimes disappointing, but in 1.2 smart reify can provide a card with non-t215> "default values".

+21
Jul 08 2018-10-16T00:
source share

Since maps are such an ubiquitous data structure in Clojure, it makes sense to have several tools to manipulate them. The various various functions are all syntactically convenient in slightly different circumstances.

My personal attention to the specific features that you mentioned:

  • I use assoc to add a single value to the map given the key and value
  • I use merge to combine two cards or add several new records at once
  • I usually don’t use conj with maps, as I mentally map them to lists
+6
Jul 08 '10 at 13:20
source share



All Articles