【问题标题】:Is there a built-in method or gem that can print a Ruby array in columns?是否有可以在列中打印 Ruby 数组的内置方法或 gem?
【发布时间】:2012-06-19 19:29:09
【问题描述】:

我有一个大的 Ruby 数组,我想在列中打印,就像 Unix' 'ls' 命令的默认输出(至少在 OS X 上)。是否有可以做到这一点的 gem 或内置方法?我知道 awesome_print gem。它有一些性感的输出,但似乎没有提供列。

【问题讨论】:

  • 手动编码应该不会太难。这是 5 行代码 :)
  • 发布一些示例数据和一些示例输出怎么样?我的水晶球今天下线了。
  • @Sergio:我在这里问这个问题的原因不是因为我不知道如何手动编码,而是因为我正在寻找标准库中的东西或我自己的 gem可以要求,以便我可以缩短自己的代码。
  • @CodeGnome:我不确定您在寻找示例代码的方式。这是一个关于那里有什么的简单问题,我不是在寻找多行代码解决方案。我意识到我应该更改标题以反映这一点。对不起你的水晶球,这也发生在我身上。

标签: ruby pretty-print


【解决方案1】:

Enumerable#each_slice 可能是你的朋友。

$ irb
irb> a = (0..18).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
irb> a.each_slice(5) { |row| puts row.map{|e| "%5d" % e}.join("  ") }
    0      1      2      3      4
    5      6      7      8      9
   10     11     12     13     14
   15     16     17     18

如果您希望它们在列中排序,您可以使用 slice 和 Enumerable#zip

irb> cols = a.each_slice((a.size+2)/3).to_a
=> [[0, 1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13], [14, 15, 16, 17, 18]]
irb> cols.first.zip( *cols[1..-1] ).each{|row| puts row.map{|e| e ? '%5d' % e : '     '}.join("  ") }
    0      7     14
    1      8     15
    2      9     16
    3     10     17
    4     11     18
    5     12       
    6     13       

【讨论】:

  • 非常漂亮。我会用这个。我仍然想知道是否有一个标准库或通用 gem 函数可以处理这个问题,也许会自动调整列间距。
  • @Sean:确实很漂亮,我喜欢那些真正教给你一些东西的答案,谢谢 dbenhur,但是你是否意识到,当你包含一个 gem 时,你可能包含了数千行代码?为什么还要花几行代码来完成这项工作呢?
【解决方案2】:

我同意评论者 Sean 的观点,但我只是无法控制自己,而是抱着我的小便来给这个可爱的小可爱一个人,我在 Windows 上,所以不知道 ls 的输出是如何相似的,但我确定那里这里的选项足以为您提供所需的输出

cm = {'headers' => ['first', 'second', 'third', 'fourth'], 'width' => [5, 5, 16, 30], 'separator' => '|', 'align' => [:left,:right,:left,:right]}
a = [["on", "two", "three", "a loooooooooooooooonger field"],["four","five","looooooooooonger","short one"]]
cm['headers'].each_with_index{|header, index|w = cm['width'][index];print "#{cm['align'][index]==:left ? header.ljust(w)[0..w-1]:header.rjust(w)[0..w-1]}#{cm['separator']}"}
puts ""
a.each do |record|
  record.each_with_index do |field, index|
    w = cm['width'][index]
    print "#{cm['align'][index]==:left ? field.ljust(w)[0..w-1]:field.rjust(w)[0..w-1]}#{cm['separator']}"
  end
  puts ""
end

给予

first|secon|third           |                        fourth|
on   |  two|three           | a loooooooooooooooonger field|
four | five|looooooooooonger|                     short one|

【讨论】:

  • 天哪,真是个可怕的小可爱。我很欣赏它可以完成工作,但有更简单的解决方案。
  • 我追求成熟和可重复使用,虽然我认为它可以做得更短,但这超出了我目前对 Ruby 的了解
【解决方案3】:

除了我的第一个完整的可配置解决方案之外,还有一个基于元素最大字符串长度的更短的解决方案

class Array
  def to_table l = []
    self.each{|r|r.each_with_index{|f,i|l[i] = [l[i]||0, f.length].max}}
    self.each{|r|r.each_with_index{|f,i|print "#{f.ljust l[i]}|"};puts ""}
  end
end

[["on", "two", "three", "a loooooooooooooooonger field"],["four","five","looooooooooonger","short one"]].to_table

给予

on  |two |three           |a loooooooooooooooonger field|
four|five|looooooooooonger|short one                    |

【讨论】:

  • 这看起来像一个不错的扩展,而且比另一个更干净。
【解决方案4】:

我觉得用hirb比较好用:

require 'hirb'

Hirb.enable :output=>{"Array"=>{:class=>Hirb::Helpers::Table}}  #=> true

[[1,2], [2,3]]
+---+---+
| 0 | 1 |
+---+---+
| 1 | 2 |
| 2 | 3 |
+---+---+
2 rows in set

[[5,6,3,4]]
+---+---+---+---+
| 0 | 1 | 2 | 3 |
+---+---+---+---+
| 5 | 6 | 3 | 4 |
+---+---+---+---+
1 row in set

【讨论】:

  • 太棒了。这正是我想要的。在您的示例中,我看到您必须为其提供一个数组数组,因此如果您只有一个一维数组,则需要首先使用 Enumerable#each_slice 。这是一件小事,但我仍然想知道:hirb 可以自动处理一维数组吗?
【解决方案5】:

@peter 解决方案的一些改进:

  1. 如果调用者不是数组数组,则引发异常;
  2. 类型转换为字符串的任何值都适用于包含字符串和数字混合的数组;
  3. 可以将任何列分隔符字符串指定为参数;
  4. 输出是一个格式化为表格的字符串,因此可以进一步处理它,并且不会破坏任何输出,如果不是预期的输出。
class Array

  # Convert an Array of arrays into a String, formatted as a table.
  def columnize(sep="  ")
    each{|r| r.is_a? Array or raise NoMethodError, "Must be called on an Array of arrays."}
    s = ""
    l = []
    each{|r|r.each_with_index{|f,i| l[i] = [l[i]||0, f.to_s.length].max}}
    each{|r|r.each_with_index{|f,i| s << "#{f.to_s.ljust l[i]}#{sep}"}; s << "\n"}
    return s
  end
 
 end
 

像这样称呼

puts [["field", 2, 3, "another field"],[4,5,"looooooooooonger","short one"]].columnize(" | ")

产生这个输出

field | 2 | 3                | another field | 
4     | 5 | looooooooooonger | short one     | 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-29
    • 1970-01-01
    • 2021-09-03
    • 2011-12-21
    • 2011-04-23
    • 2021-05-17
    • 1970-01-01
    相关资源
    最近更新 更多