Search code examples
typesclojuresetclojurescriptenumerable

Does (into) do any higher-level inference to stay idiomatic on output type?


Say I have a key-value pair I've agnostically defined as a key-value map:

(def foo {:bar "baz" :bat "squanch"})

It occurs to me at some later time to do some set operations on it, so I'll need to convert it to a relation, which the Clojure cheatsheet says is a type of set, so I go ahead and grab (into) and go to it:

(set/project (into #{} foo) [:bar])
#{{}}

Huh?

(into #{} foo)
#{[:bar "baz"] [:bat "squanch"]}

That's not what (project) expects at all. According to the docs:

;; `project` strips out unwanted key/value pairs from a set of maps. 
;; Suppose you have these descriptions of cows:

user=> (def cows #{  {:name "betsy" :id 33} {:name "panda" :id 34} })
#'user/cows

;; You care only about the names. So you can get them like this:

user=> (project cows [:name])
#{{:name "panda"} {:name "betsy"}}

So close.

Should I be expecting (into) to know what I mean when I convert into one of these types, or is there another way? If it's going to be like this, I might as well just roll my own thing with a few map/flatten calls, but that's exactly what I'm trying to avoid by phrasing things in the more elegant language of sets.

For clarity, here is an example of something I thought would be best done with (set/project) that seems not to be possible with the above expectations:

(defn exclude-keys "Filters out all but argument-provided keys from a key-value
  map."
  [input-map excluded-keys]
  (-> input-map (select-keys (set/difference (into #{} (keys input-map))
                                              excluded-keys))))

I guess I'm just surprised that it takes that much extra syntax to accomplish in Clojure.


Solution

  • into just conjs the elements of a sequence into the given collection e.g.

    (into #{} [:a :b :b :c :a])
    => #{:c :b :a}
    

    Since maps are sequences of pairs you end up with a set of the pairs in the input map.

    If you just want to remove a collection of keys from a map you can use dissoc:

    (defn exclude-keys "Filters out all but argument-provided keys from a key-value
      map."
      [input-map excluded-keys]
      (apply dissoc input-map excluded-keys))