0

I run into this problem a lot (more often in clojurescript) but haven't noticed how others deal with it. This is what I want:

[:div.container [:div.first "first"] [:div.second "second"] [:div.third "third"]]

And let's say I'm building it from a collection like: ["first" "second" "third"]

I'll pass the collection through a mapv and wind up with (this code is a sketch, I'm not using it):

(mapv
  #([(keyword (str "div." %)) %])
  ["first" "second" "third"])
=> [[:div.first "first"] [:div.second "second"] [:div.third "third"]]

And if I were to use it inside the container div, I'd get:

[:div.container [[:div.first "first"] [:div.second "second"] [:div.third "third"]]]

I've been using things like cons to put the :div.container at the front of the result of mapv. But in my mind there should be a better way--like an opposite of reduce. Is there such a thing?

Edit: I did find concat and it looks like I've tried it before, but I get a sequence back. I want a vector. (vec (concat ... )) is not ideal.

dirtymikeandtheboys
  • 511
  • 2
  • 5
  • 17
  • possible duplicate of [What is the idiomatic way to prepend to a vector in Clojure?](http://stackoverflow.com/questions/4095714/what-is-the-idiomatic-way-to-prepend-to-a-vector-in-clojure) – DaoWen May 08 '15 at 01:29

4 Answers4

4

How about:

 (apply vector :div.container 
               (mapv #(vector (keyword (str "div." %)) %) 
                     ["first", "second", "third"]))

The idea is to use apply to pass each element of the result of mapv as an individual argument to vector.

As a bonus, apply admits other arguments before the collection argument, so we can stow the :div.container there.

(Here using mapv vs. map would be indifferent.)

(Also, the "opposite" of reduce is usually called unfold in other languages, but I havent found the analogous function in the core Clojure libraries.)

danidiaz
  • 26,936
  • 4
  • 45
  • 95
  • there is an `iterate` fn (http://conj.io/store/v1/org.clojure/clojure/1.7.0-alpha4/clj/clojure.core/iterate/) in clojure which looks exactly like `unfoldr` for me – Dima May 08 '15 at 03:19
  • @Dima `unfold` is a bit more general, because it can generate finite sequences, and has an "internal state" whose "type" can be different from the type of the results. Here are two Clojure implementations: http://www.enrico-franchi.org/2011/08/clojure-unfold-and-anamorphisms.html http://www.matlux.net/blog/2014/05/04/anamorphic-adventure-in-clojure – danidiaz May 08 '15 at 06:23
1

cons is fine here -- it's been around since the dawn of Lisp and it's useful for exactly what you want to do here.

Here's how I'd approach this:

(defn containerize
  [tag v]
  (let [kw #(keyword (str tag "." %))]
    (into [] 
      (cons (kw "container")
        (map (fn [x] [(kw x) x]) v)))))
user2524973
  • 1,087
  • 6
  • 14
1

The "opposite of reduce" is reduce.

(reduce (fn [v s]
          (conj v [(keyword (str "div." s)) s]))
        [:div.container]
        ["first" "second" "third"])
noisesmith
  • 20,076
  • 2
  • 41
  • 49
0

Are you aware that [:ul ([:li "one"] [:li "two"])] will work as expected?

I think if you change (mapv f coll) to

(map f coll)

or

(doall (map f coll))

You'll get the result you're looking for.