La mejor manera de imprimir un hash bonito


169

Tengo un hash grande con matrices y hashes anidados. Me gustaría simplemente imprimirlo para que sea 'legible' para el usuario.

Me gustaría que fuera algo así como to_yaml, eso es bastante legible, pero todavía tiene un aspecto demasiado tecnológico.

En última instancia, serán los usuarios finales los que deberán leer estos fragmentos de datos, por lo que deberán formatearse de forma limpia.

¿Alguna sugerencia?



utilidad en línea jsonviewer.stack.hu . Sin embargo, no funciona correctamente para la sintaxis de hash rocket.
Amit Patel

Respuestas:


256
require 'pp'
pp my_hash

Úselo ppsi necesita una solución integrada y solo quiere saltos de línea razonables.

Usa awesome_print si puedes instalar una gema. (Dependiendo de sus usuarios, es posible que desee utilizar la index:falseopción para desactivar la visualización de índices de matriz).


pp es agradable, pero es realmente una pena que no se pueda limitar la profundidad.
akim

95

Si tiene JSON, lo recomiendo JSON.pretty_generate(hash)porque es más simple que awesome_print , se ve muy bien en una preetiqueta y permite copiarlo fácilmente desde una página web. (Consulte también: ¿Cómo puedo formatear "bastante" mi salida JSON en Ruby on Rails? )


Esta respuesta se beneficiaría de un ejemplo real
Travis Bear

@TravisBear Hay resultados de ejemplo si hace clic en el enlace "ver también" en mi respuesta. Recomiendo esta respuesta en particular: stackoverflow.com/a/1823885/109618
David J.

8
Seríaputs JSON.pretty_generate(hash)
joeloui

Si necesita crear JSON, permítame recomendarle mi propia biblioteca (gratuita, OSS, sin anuncios) para crear bonitos JSON de Ruby o JS: NeatJSON (Ruby) y NeatJSON (Online / JS)
Phrogz

Lo siento, ahora me doy cuenta de que pretty_generate acepta un objeto Ruby, no un texto json.
Tony

26

Otra solución que funciona mejor para mí que ppo awesome_print:

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

2
Tenga en cuenta que Pry::ColorPrinter.pp(obj)escribe en estándar pero puede tomar parámetros adicionales, incluido el destino. Me gustaPry::ColorPrinter.pp(obj, a_logger)
Eric Urban

Me sorprende que esto no esté mejor documentado: siempre uso palanca como mi consola Rails, y he estado buscando durante mucho tiempo cómo aprovechar su bonita impresora sin usar otra gema. Elegido porque esta solución finalmente puso fin a mi larga búsqueda. :-)
wiz

20

Si no tiene ninguna acción de gema elegante, pero sí tiene JSON, esta línea CLI funcionará en un hash:

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

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

  :key2 => "value2",

  :key3 => "value3"
}

8
Votado en contra porque esto arruinará cualquier clave y valor que contenga ":"
thomax

1
Esto tampoco trata con nulo (JSON) vs nulo (Ruby).
Rennex

1
Sigue siendo útil para la mayoría de las situaciones.
Abram

1
¡No puedo creer esto tres años después! Gracias @ Abram. :) No es la solución más elegante del mundo, pero hace las cosas en un apuro.
Nick Schwaderer

4

Use las respuestas anteriores si está imprimiendo a los usuarios.

Si solo desea imprimirlo usted mismo en la consola, le sugiero que use la gema de palanca en lugar de irb. Además de la impresión bonita, la palanca también tiene muchas otras características (consulte railscast a continuación)

joya instalar palanca

Y mira este railscast:

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


3

Fácil de hacer con json si confías en que tus claves estén sanas:

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 Pry solo necesita agregar el siguiente código a su ~ / .pryrc:

require "awesome_print"
AwesomePrint.pry!

1

De todas las gemas que probé, la show_datagema funcionó mejor para mí, ahora la uso ampliamente para registrar hash de params en Rails casi todo el tiempo


0

Para hashes anidados grandes, este script podría ser útil para usted. Imprime un hash anidado en una bonita sintaxis de python / like con solo sangrías para que sea fácil de copiar.

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

Ejemplo de uso:

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

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

El valor devuelto es el recuento (3) de todos los valores de nivel final del hash anidado.


0

Aquí hay otro enfoque usando json y 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))))

(analiza la respuesta de, p RestClient. ej. )


0

En rieles

Si necesitas

  • un Hash "muy impreso"
  • en, por ejemplo, el Rails.logger
  • que, específicamente, corre inspect en los objetos en el hash
    • lo cual es útil si anula / define el inspectmétodo en sus objetos como se supone que debe

... entonces esto funciona muy bien! (Y mejora, cuanto más grande y más anidado esté su objeto Hash).

logger.error my_hash.pretty_inspect

Por ejemplo:

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 proviene de PrettyPrint , que incluye los rieles de forma predeterminada. Por lo tanto, no se necesitan gemas ni se necesita conversión a JSON.

No en rieles

Si no está en Rails o si lo anterior falla por alguna razón, intente usar require "pp"primero. Por ejemplo:

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}

Un ejemplo completo

Gran pretty_inspectejemplo de Hash de mi proyecto con texto específico del proyecto de mis objetos inspeccionados redactados:

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

Bajo Rails, las matrices y los hashes en Ruby tienen funciones incorporadas en to_json. Usaría JSON solo porque es muy legible dentro de un navegador web, por ejemplo, Google Chrome.

Dicho esto, si le preocupa que parezca demasiado "técnico", probablemente debería escribir su propia función que reemplace las llaves y las llaves en sus hashes y matrices con espacios en blanco y otros caracteres.

Busque la función gsub para una muy buena manera de hacerlo. Sigue jugando con diferentes personajes y diferentes cantidades de espacios en blanco hasta que encuentres algo que parezca atractivo. http://ruby-doc.org/core-1.9.3/String.html#method-i-gsub


77
Las matrices y los hash no tienen un método incorporado en to_json, ActiveSupport los agrega desde Rails.
Tom De Leu

Es aún peor de lo normal irb / palanca:{"programming_language":{"ruby":{},"python":{}}}
Darek Nędza

OP no descartó Rails
Will Sheppard
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.