Melhor maneira de imprimir um hash bonito


169

Eu tenho um grande hash com matrizes e hashes aninhados. Gostaria simplesmente de imprimi-lo para que fique 'legível' para o usuário.

Eu gostaria que fosse algo como to_yaml - isso é bem legível - mas ainda parece muito tecnológico.

Por fim, serão os usuários finais que precisam ler esses blocos de dados para que sejam formatados de maneira limpa.

Alguma sugestão?



utilitário online jsonviewer.stack.hu . No entanto, ele não funciona corretamente para a sintaxe de hash rocket.
Amit Patel

Respostas:


256
require 'pp'
pp my_hash

Use ppse você precisar de uma solução integrada e apenas desejar quebras de linha razoáveis.

Use awesome_print se você pode instalar uma jóia. (Dependendo dos usuários, convém usar a index:falseopção para desativar a exibição dos índices da matriz.)


pp é legal, mas é realmente uma pena que não se possa limitar a profundidade.
akim 20/04

95

Se você possui JSON, eu recomendo, JSON.pretty_generate(hash)porque é mais simples que awesome_print , fica ótimo em uma pretag e facilita a cópia de uma página da web. (Veja também: Como posso "formatar" minha saída JSON em Ruby on Rails? )


Esta resposta se beneficiaria de um exemplo real
Travis Bear

@TravisBear Há um exemplo de saída se você clicar no link "ver também" na minha resposta. Eu recomendo esta resposta em particular: stackoverflow.com/a/1823885/109618
David J.

8
Seriaputs JSON.pretty_generate(hash)
joeloui

Se você precisar criar JSON, permita-me recomendar minha própria biblioteca (gratuita, OSS, sem anúncios) para criar bastante JSON a partir de Ruby ou JS: NeatJSON (Ruby) e NeatJSON (Online / JS)
Phrogz

Desculpe, agora percebo que pretty_generate aceita um objeto Ruby, não um texto json.
Tony

26

Outra solução que funciona melhor para mim do que ppou awesome_print:

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

2
Observe que Pry::ColorPrinter.pp(obj)grava no padrão, mas pode usar parâmetros adicionais, incluindo o destino. ComoPry::ColorPrinter.pp(obj, a_logger)
Eric Urban

Estou surpreso que isso não esteja melhor documentado: eu sempre uso o alavanca como meu console do Rails, e eu venho procurando há muito tempo como explorar sua bonita impressora sem usar outra gema. Promovido porque esta solução finalmente pôs fim à minha longa pesquisa. :-)
wiz

20

Se você não possui nenhuma ação sofisticada de gemas, mas possui JSON, esta linha de CLI funcionará em um hash:

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

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

  :key2 => "value2",

  :key3 => "value3"
}

8
Downvoted porque isso vai estragar todas as chaves e valores que contêm ":"
thomax

1
Isso também não lida com nulo (JSON) vs nulo (Ruby).
Rennex

1
Ainda é útil para a maioria das situações.
Abram

1
Não posso acreditar nisso três anos depois! Obrigado @Abram. :) Não é a solução mais elegante do mundo, mas faz as coisas de uma só vez.
Nick Schwaderer 8/10/19

4

Use as respostas acima se estiver imprimindo para os usuários.

Se você quiser imprimi-lo apenas no console, sugiro usar a alavanca de alavanca em vez do irb. Além da impressão bonita, o alavanca também possui muitos outros recursos (confira o railscast abaixo)

instalar alavanca

E verifique este railscast:

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


3

Fácil de fazer com o json, se você confiar que suas chaves são saudáveis:

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
}

1

Usando o Pry, você só precisa adicionar o seguinte código ao seu ~ / .pryrc:

require "awesome_print"
AwesomePrint.pry!

1

De todas as gemas que tentei, a show_datagem funcionou melhor para mim, agora a uso extensivamente para registrar hashes de params no Rails praticamente o tempo todo


0

Para hashes aninhados grandes, esse script pode ser útil para você. Ele imprime um hash aninhado em uma boa sintaxe semelhante a python, com apenas recuos para facilitar a cópia.

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

Exemplo de uso:

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

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

O valor retornado é a contagem (3) de todos os valores de nível final do hash aninhado.


0

Aqui está outra abordagem usando json e 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))))

(analisa a resposta de, por exemplo RestClient)


0

No Rails

Se você precisar

  • um Hash "bem impresso"
  • por exemplo, no Rails.logger
  • que, especificamente, roda inspect nos objetos no Hash
    • o que é útil se você substituir / definir o inspectmétodo em seus objetos como deveria

... então isso funciona muito bem! (E fica melhor, quanto maior e mais aninhado for o objeto Hash.)

logger.error my_hash.pretty_inspect

Por exemplo:

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_inspectvem do PrettyPrint , que os trilhos incluem por padrão. Portanto, não há necessidade de gemas nem conversão para JSON.

Não nos trilhos

Se você não está no Rails ou se o acima falhar por algum motivo, tente usar require "pp"primeiro. Por exemplo:

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}

Um exemplo completo

Big ol' pretty_inspected exemplo Hash do meu projeto com o texto específico do projeto de meus objetos inspecionados editados:

{<***::******************[**:****, ************************:****]********* * ****** ******************** **** :: *********** - *** ******* *********>=>
  {: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=>
                            ["***** ******* ******",
                             "***** ******* ******"]}}}}]}}]}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - ********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - **********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - **********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - **********>=>
            {}}]}}]}}

-4

Em Rails, matrizes e hashes no Ruby possuem funções to_json internas. Eu usaria JSON apenas porque é muito legível em um navegador da web, por exemplo, Google Chrome.

Dito isto, se você está preocupado com a aparência muito "tecnológica", provavelmente deve escrever sua própria função que substitui os chavetas e chavetas nos seus hashes e matrizes por espaços em branco e outros caracteres.

Procure a função gsub para uma maneira muito boa de fazer isso. Continue brincando com diferentes personagens e diferentes quantidades de espaço em branco até encontrar algo que pareça atraente. http://ruby-doc.org/core-1.9.3/String.html#method-i-gsub


7
Matrizes e hashes não têm um método to_json interno, eles são adicionados pelo ActiveSupport do Rails.
Tom De Leu

É ainda pior do que o normal IRB / alavanca:{"programming_language":{"ruby":{},"python":{}}}
Darek Nędza

O OP não descartou o Rails
Will Sheppard
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.