flatten

clojure.core

  • (flatten x)
Takes any nested combination of sequential things (lists, vectors,
etc.) and returns their contents as a single, flat sequence.
(flatten nil) returns nil.

1 Example top

  • user=> (flatten [1 [2 3]])
    (1 2 3)
    
    user=> (flatten '(1 2 3))
    (1 2 3)
    
    user=> (flatten '(1 2 [3 (4 5)])) 
    (1 2 3 4 5)
    
    user=> (flatten nil)
    ()
    
    ; Attention with stuff which is not a sequence
    
    user=> (flatten 5)
    ()
    
    user=> (flatten {:name "Hubert" :age 23})
    ()
    
    ; Workaround for maps
    
    user=> (flatten (seq {:name "Hubert" :age 23}))
    (:name "Hubert" :age 23)
Log in to add / edit an example.

See Also top

Log in to add a see also.

Plus_12x12 Minus_12x12 Source clojure/core.clj:6218 top

(defn flatten
  "Takes any nested combination of sequential things (lists, vectors,
  etc.) and returns their contents as a single, flat sequence.
  (flatten nil) returns nil."
  {:added "1.2"
   :static true}
  [x]
  (filter (complement sequential?)
          (rest (tree-seq sequential? seq x))))
Vars in clojure.core/flatten:
Used in 0 other vars

Comments top

4 comment(s) for flatten.

(flatten nil) actually returns an empty sequence, not nil. The doc string is fixed in 1.4.

As shown in the example, flatten will return an empty sequence when given any non-sequential thing. That can sometimes hide a bug.

Here's another version that doesn't have that problem, and is faster as well.

(defn flatten2
  "Like `clojure.core/flatten` but better, stronger, faster.
  Takes any nested combination of sequential things (lists, vectors,
  etc.) and returns their contents as a single, flat, lazy sequence.
  If the argument is non-sequential (numbers, maps, strings, nil, 
  etc.), returns the original argument."
  {:static true}
  [x]
  (letfn [(flat [coll] 
                  (lazy-seq 
                   (when-let [c (seq coll)] 
                     (let [x (first c)] 
                       (if (sequential? x) 
                         (concat (flat x) (flat (rest c))) 
                         (cons x (flat (rest c))))))))]
    (if (sequential? x) (flat x) x)))

Actually, flatten on a vector returns list, not a collection:

user=> (flatten [1 [2 3]])
(1 2 3)

lazy version is much slower than this one:

(defn my-flatten [l] 
  "free of StackOverflow problem, not lazy and much faster version of flatten."
(loop [l1 l, l2 `()]
  (cond
    (sequential? (first l1)) (recur (concat (first l1) (rest l1)) l2)
    (empty? l1) (reverse l2)
    :else (recur (rest l1) (cons (first l1) l2)))))

for complicated construction genereted by:

(defn gen-list-wird [c] (reduce (fn [a b] (list a b)) (map vector (range c) (map str (range c (* 2 c))))))

times are: core/flatten (260 msec) steveminer/flatten (135 msec) my-flatten (2 msec). This version is slower than steveminder`s version for flat and very nested structures with small number of items.

Log in to add a comment.