1
votes

I'd like to have a function, such that,

(f '([1 4 7] [2 5 9] [3 6]))

would give

([1 2 3] [4 5 6] [7 9])

I tried

(apply map vector '([1 4 7] [2 5 9] [3 6]))

would only produce:

([1 2 3] [4 5 6])

I find it hard to describe my requirements that it's difficult for me to search for a ready solution.

Please help me either to improve my description, or pointer to a solution.

Thanks in advance!

7

7 Answers

3
votes

or this way with loop/recur:

user> (defn transpose-all-2 [colls]
        (loop [colls colls res []]
          (if-let [colls (seq (filter seq colls))]
            (recur (doall (map next colls)) 
                   (conj res (mapv first colls)))
            res)))
#'user/transpose-all-2

user> (transpose-all-2 x)
[[1 2 3] [4 5 6] [7 9]]

user> (transpose-all-2 '((0 1 2 3) (4 5 6 7) (8 9)))
[[0 4 8] [1 5 9] [2 6] [3 7]]
3
votes

I'd solve a more general problem which means you might reuse that function in the future. I'd change map so that it keeps going past the smallest map.

(defn map-all
  "Like map but if given multiple collections will call the function f
   with as many arguments as there are elements still left."
  ([f] (map f))
  ([f coll] (map f coll))
  ([f c1 & colls]
   (let [step (fn step [cs]
                (lazy-seq
                  (let [ss (keep seq cs)]
                    (when (seq ss)
                      (cons (map first ss)
                            (step (map rest ss)))))))]
     (map #(apply f %) (step (conj colls c1))))))


(apply map-all vector '([1 4 7] [2 5 9] [3 6]))
(apply map-all vector '([1 false 7] [nil 5 9] [3 6] [8]))

Note, that as opposed to many other solutions, this one works fine even if any of the sequences contain nil or false.

2
votes

If you know the maximum length of the vectors ahead of time, you could define

(defn tx [colls] 
  (lazy-seq 
    (cons (filterv identity (map first colls))
          (tx (map rest colls))))) 

then

(take 3 (tx '([1 4 7] [2 5 9] [3 6])))
1
votes

A simple solution is

(defn transpose-all
  [colls]
  (lazy-seq
   (let [ss (keep seq colls)]
     (when (seq ss)
       (cons (map first ss) (transpose-all (map rest ss)))))))

For example,

(transpose-all '([1 4 7] [2 5 9] [3 6] [11 12 13 14]))
;((1 2 3 11) (4 5 6 12) (7 9 13) (14))
0
votes

Here is my own attempt:

(defn f [l]
  (let [max-count (apply max (map count l))
        l-patched (map (fn [e] (if (< (count e) max-count)
                                 (concat e (take (- max-count (count e)) (repeat nil)))
                                 e)) l)]
    (map (fn [x] (filter identity x)) (apply map vector l-patched))
    )) 
0
votes

Another simple solution:

(->> jagged-list
     (map #(concat % (repeat nil)))
     (apply map vector)
     (take-while (partial some identity)))

A jagged-list like this

'([1   4   7     ] 
  [2   5   9     ] 
  [3   6         ] 
  [11  12  13  14]) 

will produce:

'([1   2   3   11] 
  [4   5   6   12] 
  [7   9   nil 13] 
  [nil nil nil 14])
0
votes

Here is another go that doesn't require you to know the vector length in advance:

(defn padzip [& [colls]]
    (loop [acc [] colls colls]
      (if (every? empty? colls) acc
          (recur (conj acc (filterv some? 
                                    (map first colls))) (map rest colls)))))