What's the one-level sequence flattening function in Clojure?
Solution 1
My general first choice is apply concat
. Also, don't overlook (for [subcoll coll, item subcoll] item)
-- depending on the broader context, this may result in clearer code.
Solution 2
There's no standard function. apply concat
is a good solution in many cases. Or you can equivalently use mapcat seq
.
The problem with apply concat
is that it fails when there is anything other than a collection/sequential is at the first level:
(apply concat [1 [2 3] [4 [5]]])
=> IllegalArgumentException Don't know how to create ISeq from: java.lang.Long...
Hence you may want to do something like:
(defn flatten-one-level [coll]
(mapcat #(if (sequential? %) % [%]) coll))
(flatten-one-level [1 [2 3] [4 [5]]])
=> (1 2 3 4 [5])
As a more general point, the lack of a built-in function should not usually stop you from defining your own :-)
Solution 3
i use apply concat
too - i don't think there's anything else in the core.
flatten
is multiple levels (and is defined via a tree-walk, not in terms of repeated single level expansion)
see also Clojure: Semi-Flattening a nested Sequence which has a flatten-1
from clojure mvc (and which is much more complex than i expected).
update to clarify laziness:
user=> (take 3 (apply concat (for [i (range 1e6)] (do (print i) [i]))))
012345678910111213141516171819202122232425262728293031(0 1 2)
you can see that it evaluates the argument 32 times - this is chunking for efficiency, and is otherwise lazy (it doesn't evaluate the whole list). for a discussion of chunking see comments at end of http://isti.bitbucket.org/2012/04/01/pipes-clojure-choco-1.html
missingfaktor
I am no longer active on this site, but if my posts help you and/or you need further help, do let me know on Twitter at @missingfaktor. I will try my best to respond! Note: This profile description was written for StackOverflow, and was copied to all other StackExchange sites as-is.
Updated on June 15, 2022Comments
-
missingfaktor almost 2 years
What's the one-level sequence flattening function in Clojure? I am using
apply concat
for now, but I wonder if there is a built-in function for that, either in standard library or clojure-contrib. -
missingfaktor almost 12 yearsI gather
apply
should be used rarely? Is this usage justified? Isn't it slower than necessary? -
andrew cooke almost 12 yearsthe only reason i can think for saying it should be used rarely is that if a single alternative exists then it should be used. as for speed, i have no idea, but don't expect it to be particularly slow.
-
amalloy almost 12 yearsIt's definitely not the case that apply should be used rarely. Use it all you want; sprinkle it liberally on your pasta, whatever. Sequences are lazy, including function-arg sequences, so
(apply (fn [& args] (first args)) (range 1e5))
is still very fast, just passing the function a pointer to the start of the list.concat
has a similar "shape" so it's just as cheap. -
missingfaktor almost 12 yearsThank you for your answer. I want the behavior that's given by
apply concat
. -
missingfaktor almost 12 yearsOkay, I think
mapcat identity
was what I needed. Thank you for the answer! -
Timothy Pratley over 8 yearsmapcat is implemented as apply concat github.com/clojure/clojure/blob/clojure-1.7.0/src/clj/clojure/…, which is lazy through the magic of handling args as a sequence.
-
Didier A. over 5 yearsIt's worth knowing that apply realizes a minimum of 4 elements, even without chunking. stackoverflow.com/q/51959298/172272