Few general points about your code:

1. `(while condition &body)` form already treats `&body` as an implicit `progn`. There's no reason to wrap it in a `progn`.
2. `insert` accepts multiple arguments.  So, you could write `(insert (mapconcat ...) "\n")` instead of calling `(insert ...)` followed by `(newline)`.
3. Traditionally, Lisp functions use `-` when the name of the function consists of multiple words, so, it would be better to call `zip-list`, not `zip_list`.
4. `(let (res) ...)` is exactly the same as `(let ((res)) ...)` but shorter.
5. `(setq ...) can handle multiple assignments, they are executed in order they are written, so `(setq a b c a)` will assign `b` to `c`.  No reason to write `(setq a b) (setq c a)`.
6. `split-string` has argument `OMIT-NULLS` that controls whether empty strings are included in the results.  It would be better to rely on this argument than to post-process the results.
7. As mentioned in the comments, there's an easier, more idiomatic way to obtain all lines of text from the buffer: `(split-string (buffer-string) "\n")`.

Finally, unless only for the sake of an exercise in writing the `zip-list` function, the transpose operation calls for something like vector, not a list.  Below is a possible alternative which uses vectors to perform this operation:

<!-- language: lang-lisp -->

    (defun wvxvw/transpose-buffer ()
      (interactive)
      (let* ((lines (split-string (buffer-string) "\n"))
             (max-length
              (cl-loop for line in lines
                       maximize (max (length line))))
             (src (cl-coerce lines 'vector))
             (dst (cl-loop with matrix = (make-vector max-length nil)
                           with len = (length src)
                           for i below max-length do
                           (aset matrix i (make-vector len ?\ ))
                           finally (cl-return matrix))))
        (cl-loop for i upfrom 0
                 for line across src do
                 (cl-loop for j upfrom 0
                          for c across line do
                          (aset (aref dst j) i c)))
        (erase-buffer)
        (cl-loop for line across dst do
                 (insert (cl-coerce line 'string) "\n"))))