Why only vectors `into` map?

Why does into {} fail when given a list, but succeed with a vector?

(let [cols [:id :label]
      data '({:id 1 :label "one"} {:id 2 :label "two"})]
;;(into {} (map #(map % cols) data)) ;; Execution error (ClassCastException) at (REPL:1).
  (into {} (map #(mapv % cols) data))
  ;; {1 "one", 2 "two"}
  ) 

The behavior of conj with maps is:

  1. when provided a MapEntry it adds it to the map (as in associates key and value)
  2. when provided a vector, same as 1) but it treats the 0th position of the vector as the key and the 1st position as the value to be associated with the map
  3. when provided other seqables than a vector, it treats it as a seqable of MapEntries, so it’s a repeated version of 1). If the seqable does not contain MapEntries then you’ll run into a ClassCastException.

The rationale for 2 could be that creating vectors is much more ergonomic than having to create MapEntry instances.

The rationale for 3 could be that these kinds of operations work:

(conj {:a 1} (filter (fn [[k v]] (odd? v)) {:b 2 :c 3}))
;;=> {:a 1, :c 3}

Now I’ll wait for Alex to give the official answer :P.

5 Likes

LGTM borkdude :slight_smile:

I guess the only thing to add is that a list of 2 elements is not treated as a valid map entry because it is not indexed (you can’t just grab the 0th key “slot” and the 1th value “slot”), you would have to iterate through the key to get to the value.

5 Likes

Ah; so lists fail ducktyping as vectors so are handled differently on map conj. I can remember that. Thanks!

Probably worth the clarification that it has to be a 2-element vector. If the vector has 3 or more elements, you’ll get Vector arg to map conj must be a pair (IllegalArgumentException).

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.