Reputation: 3
New to Clojure.
Input - ["a" ["b" "c"] "d"]
Expected output - ["a" "b" "c" "d"]
What I'm trying to do - create an empty vector ('result'), then do two doseq's on the input collection to fill 'result' up, finally return the filled up 'result'. However the function returns an empty vector. What am I doing wrong?
(flat ["a" ["b" "c"] "d"])
(defn flat [arr]
(let [result []]
(doseq [element arr]
(if (coll? element) (doseq [element2 element] (conj result element2))
(conj result element))) result))
Upvotes: 0
Views: 209
Reputation: 1107
If you want a fast version of flatten, see clojure.core.reducers/flatten.
(require '[clojure.core.reducers :as r])
(defn rflatten [coll] (into [] (r/flatten coll)))
Upvotes: 0
Reputation:
#!/usr/bin/env boot
(def inp ["a" "b" ["c" "d" "e" ["f" "g" "h"] "i"] "j" "k"])
(defn flat
([xs] (flat xs []))
([xs acc]
(if (empty? xs) acc
(if (vector? (first xs))
(flat (rest xs) (flat (first xs) acc))
(recur (rest xs) (conj acc (first xs)))))))
(println (flat inp)) ;[a b c d e f g h i j k]
The basic idea is to check if first element is a list, if so recurse that list (flat (first xs) acc)
adding each element to the accumulator and then proceed with rest of the list giving (flat (rest xs) (flat (first xs) acc))
. Else just recur individual elements.
We can use other constructs like let
, cond
as well.
Upvotes: 0
Reputation: 1057
As others have pointed out, you can't mutate result. If you really did want to implement your function with mutation you'd need an atom, which you can mutate with swap!
(defn flat [arr]
(let [result (atom [])]
(doseq [element arr]
(if (coll? element) (doseq [element2 element] (swap! result conj element2))
(swap! result conj element)))
@result))
Notice however, that this only gives you a single level of flattening, which you can accomplish simply with
(apply concat <your seq>)
A simple, recursive, multilevel flatten is:
(defn flat [x] (if (coll? x) (mapcat flat x) [x]))
Upvotes: 1