使用IO.ANSI输出表格数据

时间:2015-06-10 07:07:25

标签: elixir

我想将一个二维列表呈现给一个很好的表格输出,使用ANSI转义序列来控制格式。

所以给出了这个数据:

100  20   30
20   10   20
50   400  20

我想输出这样的内容:

{{1}}

非常感谢

4 个答案:

答案 0 :(得分:3)

如果每个项目的长度小于7个字符,则可以使用制表符来实现间距:

iex> Enum.each(data, fn (x) -> Enum.join(x, "\t") |> IO.puts end)
100     20      30
20      10      20
50      400     20
:ok

答案 1 :(得分:3)

这是一个更精细的解决方案,适用于任意宽的值:

defmodule Table do
  def format(rows, opts \\ []) do
    padding = Keyword.get(opts, :padding, 1)
    rows = stringify(rows)
    widths = rows |> transpose |> column_widths
    rows |> pad_cells(widths, padding) |> join_rows
  end

  defp pad_cells(rows, widths, padding) do
    Enum.map rows, fn row ->
      for {val, width} <- Enum.zip(row, widths) do
        String.ljust(val, width + padding)
      end
    end
  end

  def join_rows(rows) do
    rows |> Enum.map(&Enum.join/1) |> Enum.join("\n")
  end

  defp stringify(rows) do
    Enum.map rows, fn row ->
      Enum.map(row, &to_string/1)
    end
  end

  defp column_widths(columns) do
    Enum.map columns, fn column ->
      column |> Enum.map(&String.length/1) |> Enum.max
    end
  end

  # http://stackoverflow.com/questions/23705074
  defp transpose([[]|_]), do: []
  defp transpose(rows) do
    [Enum.map(rows, &hd/1) | transpose(Enum.map(rows, &tl/1))]
  end
end

像这样使用:

Table.format(data, padding: 2) |> IO.puts

打印:

100  20   30
20   10   20
50   400  20

答案 2 :(得分:3)

"Programming Elixir" book by Dave Thomas中有类似于你需要的运动:

  

编写代码以将数据格式化为列,如本章开头的示例输出:

 #   | Created at           | Title                                             
-----+----------------------+-------------------------------------------------- 
2722 | 2014-08-27T04:33:39Z | Added first draft of File.mv for moving files aro 
2728 | 2014-08-29T14:28:30Z | Should elixir (and other applications) have stick
-----+----------------------+--------------------------------------------------

并且a place where readers can post their solutions to this exercise您可以找到并选择适合您的套房。您需要修改代码,以使其与输入数据结构(数组数组,矩阵)一起使用,但这应该很容易。如果你有任何麻烦,请问。

这是我在阅读本书时写的解决方案:

defmodule Issues.TableFormatter do

  import Enum, only: [map: 2, max: 1, zip: 2, join: 2]

  @header ~w(number created_at title)
  @header_column_separator "-+-"

  # TODO: Refactor; try to find more uses for stdlib functions
  def print_table(rows, header \\ @header) do
    table = rows |> to_table(header)
    header = header |> map(&String.Chars.to_string/1) # the headers needs now to be a string
    columns_widths = [header | table] |> columns_widths

    hr = for _ <- 1..(length(header)), do: "-"

    hr     |> print_row(columns_widths, @header_column_separator)
    header |> print_row(columns_widths)
    hr     |> print_row(columns_widths, @header_column_separator)
    table  |> map &(print_row(&1, columns_widths))
    hr     |> print_row(columns_widths, @header_column_separator)
  end

  def to_table(list_of_dicts, header) do
    list_of_dicts
    |> select_keys(header)
    |> map(fn (dict) ->
      dict
      |> Dict.values
      |> map(&String.Chars.to_string/1)
    end)
  end

  def columns_widths(table) do
    table
    |> Matrix.transpose
    |> map(fn (cell) ->
      cell
      |> map(&String.length/1)
      |> max
    end)
  end

  def select_keys(dict, keys) do
    for entry <- dict do
      {dict1, _} = Dict.split(entry, keys)
      dict1
    end
  end

  def print_row(row, column_widths, separator \\ " | ") do
    padding = separator |> String.to_char_list |> List.first # Hack
    IO.puts  row
    |> zip(column_widths)
    |> map(fn ({ cell, column_width }) ->
      String.ljust(cell, column_width, padding)
    end)
    |> join(separator)
  end
end

将所有这些视为灵感而非直接解决您的问题。这也可能更符合您的需求,但能够以通用方式快速格式化某些表格数据并在您的终端中打印它们对您来说非常方便。

答案 3 :(得分:0)

有一个针对Elixir的printf库,如果你这样做会使这种事情变得更简单 在Erlang字符串格式化中不熟悉。

https://github.com/parroty/exprintf