I was working on a Rails template and was trying to write a bit of code that allows me to populate a table or multiple columns of ul tags "top-to-bottom" and "left-to-right" across however many columns I specify. I'm just getting the hang of Ruby so I couldn't figure this out. I'm also curious about an idiomatic Haskell version for this useful snippet. Improvements to Clojure version appreciated:
(defn table [xs & {:keys [cols direction]
                   :or   {cols 1 direction 'right}}]
  (into []
        (condp = direction
          'down (let [c (count xs)
                      q (int (/ c cols))
                      n (if (> (mod c q) 0) (inc q) q)]
                  (apply map vector (partition n n (repeat nil) xs)))
          'right (map vec (partition cols cols (repeat nil) xs))))) 
With this bit of code I can then do the following:
(table (range 10) :cols 3)
Printed out this would look like so:
0    1    2 
3    4    5 
6    7    8
9
And the trickier one:
(table (range 10) :cols 3 :direction 'down)
Looks like so:
0    4    8    
1    5    9    
2    6        
3    7        
                I would probably write something like this in Haskell, using the Data.List.Split package from Hackage:
import Data.List       (intercalate, transpose)
import Data.List.Split (splitEvery)
data Direction = Horizontal | Vertical deriving (Eq, Read, Show)
table :: Direction -> Int -> [a] -> [[a]]
table Horizontal cols xs = splitEvery cols xs
table Vertical   cols xs = let (q,r) = length xs `divMod` cols
                               q'    = if r == 0 then q else q+1
                           in transpose $ table Horizontal q' xs
showTable :: Show a => [[a]] -> String
showTable = intercalate "\n" . map (intercalate "\t" . map show)
main :: IO ()
main = mapM_ putStrLn [ showTable $ table Horizontal 3 [0..9]
                      , "---"
                      , showTable $ table Vertical   3 [0..9] ]
Some of this, like the Direction type and the transpose trick, was derived from jkramer's answer.  I wouldn't use keyword arguments for something like this in Haskell (it doesn't really have such things, but you can emulate them using records as in Edward Kmett's answer), but I put those arguments first because it's more useful with partial application (defaultTable = table Horizontal 1).  The splitEvery function just chunks a list into lists of the appropriate size; the rest of the code should be straightforward.  The table function returns a list of lists; to get a string, the showTable function inserts tabs and newlines.  (The intercalate function concatenates a list of lists, separating them with the given list.  It's analogous to Perl/Python/Ruby's join, only for lists instead of just strings.)
I can't read the clojure code (I've never used the language), but based on the examples, here's how I'd do it in Ruby.
def table array, cols, direction
   if direction==:down
      if array.size%cols != 0
         array[(array.size/cols+1)*cols-1]=nil
         #putting nil in the last space in the array
         #also fills all of the spaces before it
      end
      newarray=array.each_slice(array.size/cols).to_a
      table newarray.transpose.flatten(1), cols, :across
   elsif direction==:across
      array.each_slice(cols) do |row|
         puts row.join("  ")
      end
   else
      raise ArgumentError
   end
end
                        Here is something I quickly hacked in Haskell. I'm sure it's buggy and can be optimized, but it's something to start with:
import System.IO
import Data.List
data Direction = Horizontal | Vertical
main = do
    putStrLn $ table [1..9] 3 Horizontal
    putStrLn "---"
    putStrLn $ table [1..9] 3 Vertical
table xs ncol direction =
    case direction of
        Horizontal -> format (rows strings ncol)
        Vertical -> format (columns strings ncol)
    where
        format = intercalate "\n" . map (intercalate " ")
        strings = map show xs
        rows xs ncol =
            if length xs > ncol
                then take ncol xs : rows (drop ncol xs) ncol
                else [xs]
        columns xs = transpose . rows xs
Output:
1 2 3
4 5 6
7 8 9
---
1 4 7
2 5 8
3 6 9
                        My ruby solution
def table(values)
  elements = values[:elements]
  cols = values[:cols]
  rows = (elements.count / cols.to_f).ceil
  erg = []
  rows.times do |i|
    cols.times do |j|
      erg << elements[values[:direction] == 'down' ? i+(rows*j) : j+i*(rows-1)]
      if erg.length == cols
        yield erg
        erg = []
      end        
    end
  end
  yield erg
end
Usage and output:
table(:elements => [0,1,2,3,4,5,6,7,8,9], :cols => 3) do |h,i,j|
  puts h.to_s << " " << i.to_s << " " << j.to_s
end
puts "---"
table(:elements => [0,1,2,3,4,5,6,7,8,9], :cols => 3, :direction => "down") do |h,i,j|
  puts h.to_s << " " << i.to_s << " " << j.to_s
end
0 1 2
3 4 5
6 7 8
9  
---
0 4 8
1 5 9
2 6 
3 7 
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With