2010-09-15 7 views
51

Me gustaría analizar un archivo CSV para que cada fila se trate como un objeto y la fila de encabezado sea el nombre de los atributos en el objeto. Podría escribir esto, pero estoy seguro de que ya está ahí afuera.Analizar archivo CSV con campos de encabezado como atributos para cada fila

Aquí es mi entrada CSV:

"foo","bar","baz" 
1,2,3 
"blah",7,"blam" 
4,5,6 

El código sería algo como esto:

CSV.open('my_file.csv','r') do |csv_obj| 
    puts csv_obj.foo #prints 1 the 1st time, "blah" 2nd time, etc 
    puts csv.bar  #prints 2 the first time, 7 the 2nd time, etc 
end 

Con módulo CSV de Ruby Creo que sólo puede acceder a los campos de índice. Creo que el código anterior sería un poco más legible. ¿Algunas ideas?

Respuesta

89

El uso de Ruby 1.9 y superiores, se puede obtener una un objeto indexable:

CSV.foreach('my_file.csv', :headers => true) do |row| 
    puts row['foo'] # prints 1 the 1st time, "blah" 2nd time, etc 
    puts row['bar'] # prints 2 the first time, 7 the 2nd time, etc 
end 

No es Sintaxis con puntos, pero es mucho más agradable que trabajar con índices numéricos.

Como un aparte, para Ruby 1.8.x FasterCSV es lo que necesita para usar la sintaxis anterior.

+0

FasterCSV se incorporó a Ruby, creo que fue en Ruby 1.9+. –

25

Aquí hay un ejemplo de la sintaxis simbólica usando Ruby 1.9. En los ejemplos a continuación, el código lee un archivo CSV llamado data.csv del directorio Rails db.

:headers => true trata la primera fila como un encabezado en lugar de una fila de datos. El parámetro :header_converters => :symbolize luego convierte cada celda en la fila de encabezado en el símbolo de Ruby.

CSV.foreach("#{Rails.root}/db/data.csv", {:headers => true, :header_converters => :symbol}) do |row| 
    puts "#{row[:foo]},#{row[:bar]},#{row[:baz]}" 
end 

en Ruby 1,8:

require 'fastercsv' 
CSV.foreach("#{Rails.root}/db/data.csv", {:headers => true, :header_converters => :symbol}) do |row| 
    puts "#{row[:foo]},#{row[:bar]},#{row[:baz]}" 
end 

Basado en el CSV proporcionada por el Poul (el autor de la pregunta StackOverflow), la salida del código de ejemplo anterior será:

1,2,3 
blah,7,blam 
4,5,6 

Dependiendo En los caracteres utilizados en los encabezados del archivo CSV, puede ser necesario dar salida a los encabezados para ver cómo CSV (FasterCSV) convirtió los encabezados de cadena en símbolos. Puede generar la matriz de encabezados desde CSV.foreach.

row.headers 
+0

Así que cargué el archivo CSV en una matriz con solo 'allstocks << row' dentro del ciclo. ¿Cómo leo una celda 'myrow [: company]' donde 'myrow [: ticker] ==" ANAD "'? Solo hay un registro y 'ticker' es mi campo clave de todos modos. – Marcos

+0

Marcos - Si el CSV se ha convertido en una matriz, es posible que haya perdido los valores hash (símbolos). Si este es el caso, simplemente haga referencia a la celda por el número de columna, p. myrow [0]. – scarver2

2

Aunque soy bastante tarde a la discusión, hace unos meses empecé un "CSV para el objeto del asignador" en https://github.com/vicentereig/virgola.

Teniendo en cuenta el contenido de su CSV, asignándolos a una serie de FooBar objetos es bastante sencillo:

"foo","bar","baz" 
1,2,3 
"blah",7,"blam" 
4,5,6 
require 'virgola' 

class FooBar 
    include Virgola 

    attribute :foo 
    attribute :bar 
    attribute :baz 
end 

csv = <<CSV 
"foo","bar","baz" 
1,2,3 
"blah",7,"blam" 
4,5,6 
CSV 

foo_bars = FooBar.parse(csv).all 
foo_bars.each { |foo_bar| puts foo_bar.foo, foo_bar.bar, foo_bar.baz } 
+0

Acabo de descubrir que muchos de estos ya se pueden lograr con los métodos 'load' y' dump' (Ruby 1.9/FasterCSV) https://github.com/JEG2/faster_csv/blob/master/test/tc_serialization.rb –

+0

Algo Me gusta esto. ¡Es una característica realmente genial! https://gist.github.com/3188109 –

1

fácil conseguir un hash en Ruby 2.3:

CSV.foreach('my_file.csv', headers: true, header_converters: :symbol) do |row| 
    puts row.to_h[:foo] 
    puts row.to_h[:bar] 
end 
0

Desde Llegué a esta pregunta con cierta frecuencia:

array_of_hashmaps = CSV.read("path/to/file.csv", headers: true) 
puts array_of_hashmaps.first["foo"] # 1 

Esta es la versión sin bloque, cuando desea sorber todo el archivo.

Cuestiones relacionadas