Reputation: 862
I understand that doseq
will iterate over each key/val of a { ... }
literal, as seen in this example: How to Iterate Over Map Keys and Values? However, doseq
returns nil
and I'd like to both iterate and return a value.
I have a key/val object that looks like this:
{:keyAAA [ val_element_one val_element_two val_element_three ]
:keyBBB [ val_element_four val_element_five val_element_six ]
:keyCCC [ val_element_seven val_element_eight val_element_nine ]}
and I'd like to create a function that returns a value like this:
( :keyAAA :keyBBB :keyCCC )
I know I could use (keys map)
for this particular example, but I am trying to do something more complicated with each key/val "element". I need a function that iterates over a key/val object and returns a list/array filled with each "element". Specifically, I'd like to end up returning something that looks like this:
[ val_element_one
val_element_two
val_element_three
val_element_four
val_element_five
val_element_six
val_element_seven
val_element_eight
val_element_nine ]
Basically, I am looking for map (or even better reduce) type functionality over each key/val of an object. I would be very grateful for help you could share.
Upvotes: 0
Views: 1626
Reputation: 5766
If all you really want to do is concatenate the values in the map, then just use vals
and concat
:
(apply concat (vals <your-map>))
If it's actually more complex than this, you can use mapcat
. Here's how you could use mapcat
to concatenate the values:
(mapcat (fn [[k v]] ;; Or just use second
v)
<your-map>)
Upvotes: 0
Reputation: 9266
Since the accepted answer already provides the classical way to do what you are asking for consider these alternative ways using more modern language features built around transducers.
If you really want an array (like asked in the question), use eduction to get rid of the lazy sequence per-step overhead of concat
:
(into-array (eduction cat (vals your-map)))
Eduction is a collection type that produces the transduced result per consumption and is particulary useful in this case as it is directly reduced into an array (via into-array) and reduces itself efficiently
If you don't want an array but a Clojure vector, you can still get away without the lazy overhead using transducers using
(into [] cat (vals your-map))
Here, aside from getting rid of the lazy per-step overhead, the implementation also internally uses Clojure transient datastructures to speed up the insertion operation.
Note that both ways of doing this enable further extension using transducer composition. You don't have to understand how transducers are implemented to understand how to use them, it suffices to know that you can comp
them, e. g.
(into [] (comp cat (filter pos?)) (vals your-map)) ; just the positive elements
Finally, if you realize that you do need laziness at a later point, it is easy to get that back without rewriting any code:
(sequence (comp cat (filter pos?)) (vals your-map))
;; now it's up to the consumer of your collection how many
;; elements will be calculated
Upvotes: 2
Reputation: 13473
We can handle your specific problem as follows:
(def data
'{
:keyAAA [ val_element_one val_element_two val_element_three ]
:keyBBB [ val_element_four val_element_five val_element_six ]
:keyCCC [ val_element_seven val_element_eight val_element_nine ]
})
(->> data
vals
(apply concat)
vec)
;[val_element_one val_element_two val_element_three val_element_four val_element_five val_element_six val_element_seven val_element_eight val_element_nine]
But I suspect that you have something more complicated in mind. In that case, you may find the functions on this page from the Clojure Cookbook helpful. These produce maps from maps.
If you are looking for something like doseq
that does more than work by side effects, maybe you are looking for for
.
Upvotes: 4