最好的打印散列的方法

我有一个大哈希嵌套数组和哈希。我想简单地打印出来,所以它'可读'的用户。

我希望它有点像to_yaml -这是相当可读的-但仍然太高科技看起来。

最终需要读取这些数据块的将是最终用户,因此它们需要被清晰地格式化。

有什么建议吗?

164468 次浏览

在Rails中,Ruby中的数组和哈希有内置的to_json函数。我会使用JSON,因为它在web浏览器中是非常可读的,例如谷歌Chrome。

也就是说,如果你担心它看起来太“科技”,你可能应该写自己的函数,用空格和其他字符替换散列和数组中的花括号和方括号。

在gsub函数中找到一个很好的方法。继续使用不同的字符和不同数量的空白,直到你找到一些看起来有吸引力的东西。http://ruby-doc.org/core-1.9.3/String.html#method-i-gsub

require 'pp'
pp my_hash

如果你需要一个内置的解决方案,只是想要合理的换行符,请使用pp

如果可以安装gem,请使用awesome_print。(取决于你的用户,你可能希望使用index:false选项来关闭显示数组下标。)

如果你有JSON,我推荐JSON.pretty_generate(hash),因为它比awesome_print更简单,在pre标记中看起来很棒,并且允许轻松地从网页复制。(参见:我怎样才能“漂亮”?格式我的JSON输出在Ruby on Rails?)

如果要打印给用户,请使用上面的答案。

如果你只想在控制台为自己打印,我建议使用撬宝石而不是irb。除了漂亮的印刷,撬还有很多其他功能(查看下面的railscast)

宝石安装撬

看看这个铁路广播:

http://railscasts.com/episodes/280-pry-with-rails

使用Pry,你只需要添加以下代码到你的~/.pryrc:

require "awesome_print"
AwesomePrint.pry!

另一个解决方案比ppawesome_print更适合我:

require 'pry' # must install the gem... but you ALWAYS want pry installed anyways
Pry::ColorPrinter.pp(obj)

在我尝试的所有宝石中,show_data宝石对我来说是最好的,我现在广泛使用它来记录Rails中的参数散列

如果你没有任何花哨的宝石操作,但有JSON,这个CLI行将适用于哈希:

puts JSON.pretty_generate(my_hash).gsub(":", " =>")

#=>
{
:key1 => "value1",


:key2 => "value2",


:key3 => "value3"
}

下面是使用json和rouge的另一种方法:

require 'json'
require 'rouge'


formatter = Rouge::Formatters::Terminal256.new
json_lexer = Rouge::Lexers::JSON.new


puts formatter.format(json_lexer.lex(JSON.pretty_generate(JSON.parse(response))))

(解析来自例如RestClient的响应)

如果你相信你的键是正常的,json很容易做到:

JSON.pretty_generate(a: 1, 2 => 3, 3 => nil).
gsub(": null", ": nil").
gsub(/(^\s*)"([a-zA-Z][a-zA-Z\d_]*)":/, "\\1\\2:"). # "foo": 1 -> foo: 1
gsub(/(^\s*)(".*?"):/, "\\1\\2 =>") # "123": 1 -> "123" => 1


{
a: 1,
"2" => 3,
"3" => nil
}

对于大型嵌套散列,此脚本可能对您有帮助。它以类似python的漂亮语法打印嵌套散列,仅缩进以方便复制。

module PrettyHash
# Usage: PrettyHash.call(nested_hash)
# Prints the nested hash in the easy to look on format
# Returns the amount of all values in the nested hash


def self.call(hash, level: 0, indent: 2)
unique_values_count = 0
hash.each do |k, v|
(level * indent).times { print ' ' }
print "#{k}:"
if v.is_a?(Hash)
puts
unique_values_count += call(v, level: level + 1, indent: indent)
else
puts " #{v}"
unique_values_count += 1
end
end
unique_values_count
end
end

使用示例:

  h = {a: { b: { c: :d }, e: :f }, g: :i }
PrettyHash.call(h)


a:
b:
c: d
e: f
g: i
=> 3

返回值是嵌套散列的所有结束级值的计数(3)。

在Rails中

如果你需要

  • “漂亮的印刷”;哈希
  • 例如,Rails.logger
  • ,具体来说,在哈希中的对象上运行inspect
    • 这是有用的,如果你覆盖/定义inspect方法在你的对象,就像你应该

... 那么这个工作很棒!(如果你的Hash对象越大,嵌套越多,效果会更好。)

logger.error my_hash.pretty_inspect

例如:

class MyObject1
def inspect
"<#{'*' * 10} My Object 1 #{'*' * 10}>"
end
end


class MyObject2
def inspect
"<#{'*' * 10} My Object 2 #{'*' * 10}>"
end
end


my_hash = { a: 1, b: MyObject1.new, MyObject2.new => 3 }


Rails.logger.error my_hash
# {:a=>1, :b=><********** My Object 1 **********>, <********** My Object 2 **********>=>3}


# EW! ^


Rails.logger.error my_hash.pretty_inspect
# {:a=>1,
#  :b=><********** My Object 1 **********>,
#  <********** My Object 2 **********>=>3}

pretty_inspect来自PrettyPrint, rails默认包含它。因此,不需要宝石,也不需要转换为JSON。

不在Rails中

如果你不是在Rails中,或者如果上面的失败由于某种原因,首先尝试使用require "pp"。例如:

require "pp"  # <-----------


class MyObject1
def inspect
"<#{'*' * 10} My Object 1 #{'*' * 10}>"
end
end


class MyObject2
def inspect
"<#{'*' * 10} My Object 2 #{'*' * 10}>"
end
end


my_hash = { a: 1, b: MyObject1.new, MyObject2.new => 3 }


puts my_hash
# {:a=>1, :b=><********** My Object 1 **********>, <********** My Object 2 **********>=>3}


# EW! ^


puts my_hash.pretty_inspect
# {:a=>1,
#  :b=><********** My Object 1 **********>,
#  <********** My Object 2 **********>=>3}

完整示例

大ol ` pretty_inspected哈希示例从我的项目与项目特定的文本从我的检查对象编校:

{<***::******************[**:****, ************************:****]********* * ****** ******************** **** :: *********** - *** ******* *********>=>
{:errors=>
["************ ************ ********** ***** ****** ******** ***** ****** ******** **** ********** **** ***** ***** ******* ******",
"************ ************ ********** ***** ****** ******** ***** ****** ******** **** ********** is invalid",
"************ ************ ********** ***** ****** ******** is invalid",
"************ ************ ********** is invalid",
"************ ************ is invalid",
"************ is invalid"],
:************=>
[{<***::**********[**:****, *************:**, ******************:*, ***********************:****] :: **** **** ****>=>
{:************=>
[{<***::***********[**:*****, *************:****, *******************:**]******* :: *** - ******* ***** - *>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: *** - *>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: ********* - *>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: ********** - ********** *>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: ******** - *>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: **** - *******>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: *** - ********** ***** - *>=>
{}}]}},
{<***::**********[**:****, *************:**, ******************:*, ***********************:****] ******************** :: *** - *****>=>
{:errors=>
["************ ********** ***** ****** ******** ***** ****** ******** **** ********** **** ***** ***** ******* ******",
"************ ********** ***** ****** ******** ***** ****** ******** **** ********** is invalid",
"************ ********** ***** ****** ******** is invalid",
"************ ********** is invalid",
"************ is invalid"],
:************=>
[{<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - ********>=>
{}},
{<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - ********>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *******>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]*********** :: ****>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *******>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *********>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *******>=>
{:errors=>
["********** ***** ****** ******** ***** ****** ******** **** ********** **** ***** ***** ******* ******",
"********** ***** ****** ******** ***** ****** ******** **** ********** is invalid",
"********** ***** ****** ******** is invalid",
"********** is invalid"],
:**********************=>
[{<***::*******************[**:******, ************************:***]****-************ ******************** ***: * :: *** - ***** * ****** ** - ******* * **: *******>=>
{:errors=>
["***** ****** ******** **** ********** **** ***** ***** ******* ******",
"***** ****** ******** **** ********** is invalid"],
:***************=>
[{<***::********************************[**:******, *************:******, ***********:******, ***********:"************ ************"]** * *** * ****-******* * ******** * ********* ******************** *********************: ***** :: "**** *" -> "">=>
{:errors=>["**** ***** ***** ******* ******"],
:**********=>
{<***::*****************[**:******, ****************:["****** ***", "****** ***", "****** ****", "******* ***", "******* ****", "******* ***", "****"], **:""] :: "**** *" -> "">=>
{:errors=>
["***** ******* ******",
"***** ******* ******"]}}}}]}}]}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *********>=>
{}},
{<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *********>=>
{}},
{<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - ********>=>
{}},
{<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - **********>=>
{}},
{<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - **********>=>
{}},
{<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - **********>=>
{}}]}}]}}

我是通过一个搜索引擎来寻找一种以人类可读的格式打印哈希值给最终用户的方法的——特别是键中带有下划线的哈希值。

以下是我最终使用Rails 6.0.3.4所做的事情:

hash.map do |key, val|
key.to_s.humanize + ': ' + val.to_s
end.join('; ')


# Turns {:foo_bar => 'baz', :fee_ber => :bez} into 'Foo bar: Baz; Fee ber: Bez'.

Pretty Print Hash使用纯Ruby(没有宝石)

我看到这个帖子是为了自己解决这个问题。

我有一个很大的Hash,我想把它做得漂亮,但我需要使用ruby哈希符号,而不是JSON。

这是代码+示例

  • 使用pretty_generate来获得格式化好的JSON字符串。
  • 将所有JSON键替换为symbol:等效
puts JSON.pretty_generate(result)
.gsub(/(?:\"|\')(?<key>[^"]*)(?:\"|\')(?=:)(?:\:)/) { |_|
"#{Regexp.last_match(:key)}:"
}

示例JSON

{
"extensions": {
"heading": "extensions",
"take": "all",
"array_columns": [
"name"
]
},
"tables": {
"heading": "tables",
"take": "all",
"array_columns": [
"name"
]
},
"foreign_keys": {
"heading": "foreign_keys",
"take": "all",
"array_columns": [
"name"
]
},
"all_indexes": {
"heading": "all_indexes",
"take": "all",
"array_columns": [
"name"
]
},
"keys": {
"heading": "keys",
"take": "all",
"array_columns": [
"name"
]
}
}

Ruby哈希示例

{
extensions: {
heading: "extensions",
take: "all",
array_columns: [
"name"
]
},
tables: {
heading: "tables",
take: "all",
array_columns: [
"name"
]
},
foreign_keys: {
heading: "foreign_keys",
take: "all",
array_columns: [
"name"
]
},
all_indexes: {
heading: "all_indexes",
take: "all",
array_columns: [
"name"
]
},
keys: {
heading: "keys",
take: "all",
array_columns: [
"name"
]
}
}