3

I have the following variable

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

and want to return

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

and if input is [[1 2] [3 4] [5 6] [7 8 9]] then the required result is

[[1 3 5 7] [2 4 6 8] [9]]

How to do it in clojure?

3
  • You could use something like (apply mapv vector a). Though, it won't work for your second example. Commented Mar 24, 2015 at 13:21
  • possible duplicate of matrix transposition in clojure Commented Mar 24, 2015 at 15:29
  • 1
    Your requirements for uneven matrices is very strange. Your example input will produce the same output as, say, [[1 2] [3 4 9] [5 6] [7 8]], but matrix transposition is supposed to be an invertible operation. How important is it to support these broken "matrices"? If that requirement is dropped, then I would take @schaueho's advice and close this as a duplicate. Commented Mar 24, 2015 at 20:50

5 Answers 5

5
(persistent!
  (reduce
   (fn [acc e]
     (reduce-kv
      (fn [acc2 i2 e2]
        (assoc! acc2 i2 ((fnil conj []) (get acc2 i2) e2)))
      acc
      e))
   (transient [])
   [[1 2 3] [:a :b] [\a] [111] [200 300 400 500]]))

;;=> [[1 :a \a 111 200] [2 :b 300] [3 400] [500]]

An empty vector can be updated via the update-in fn at the 0th index, a non-empty vector can be, additionally, updated at the index immediately following the last value.

The reduction here is about passing the outer accumulator to the inner reducing function, updating it accordingly, and then returning it back to the outer reducing function, which in turn will pass again to the inner rf for processing the next element.

EDIT: Updated to fastest version.

Sign up to request clarification or add additional context in comments.

Comments

2

I like ifett's implementation, though it seems weird to use reduce-kv to build a vector that could be easily build with map/mapv.

So, here is how I would've done it:

(defn transpose [v]
  (mapv (fn [ind]
          (mapv #(get % ind)
                (filter #(contains? % ind) v)))
        (->> (map count v)
             (apply max)
             range)))

Comments

1
(->> (range)
     (map (fn [i]
            (->> a
                 (filter #(contains? % i))
                 (map #(nth % i)))))
     (take-while seq))

Notice that this algorithm creates a lazy seq of lazy seqs so you that you will only pay for the transformations you really consume. If you insist on creating vectors instead, wrap the forms in vec at the necessary places - or if you are using Clojurescript or don't mind a Clojure 1.7 alpha use transducers to create vectors eagerly without paying for laziness or immutability:

(into []
      (comp
       (map (fn [i]
              (into [] (comp (filter #(contains? % i))
                             (map #(nth % i)))
                    a)))
       (take-while seq))
      (range))

1 Comment

Nice solution, though it won't be able to process some inputs with nil values, e.g. [[1 nil 2] [:a nil :b]].
1

I find this easy to understand:

(defn nth-column [matrix n]
  (for [row matrix] (nth row n)))

(defn transpose [matrix]
  (for [column (range (count (first matrix)))]
    (nth-column matrix column)))

(transpose a)
=> ((1 3 5) (2 4 6))

nth-column is a list comprehension generating a sequence from the nth element of each sequence (of rows).

Then transpose-matrix is simply iterating over the columns creating a sequence element for each, consisting of (nth-column matrix column) i.e. the sequence of elements for that column.

Comments

-1
(map 
    (partial filter identity) ;;remove nil in each sub-list
    (take-while 
        #(some identity %) ;;stop on all nil sub-list
        (for [i (range)] 
            (map #(get % i) a)))) ;; get returns nil on missing values

Use get to have nil on missing values, iterate (for) on an infinite range, stop on all nil sub-list, remove nil from sub-lists. Add vector constructor before first map and in it's function (first argument) if you really need vectors.

EDIT: please leave a comment if you think this is not useful. We can all learn from mistakes.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.