importer à partir de CSV dans un tableau Ruby, avec le 1er champ comme clé de hachage, puis rechercher la valeur d'un champ donnée ligne d'en-tête

Peut-être que quelqu'un peut m'aider.

Commençant par un fichier CSV comme ceci:

Ticker,"Price","Market Cap"
ZUMZ,30.00,933.90
XTEX,16.02,811.57
AAC,9.83,80.02

Je parviens à les lire dans un tableau:

require 'csv'
tickers = CSV.read("stocks.csv", {:headers => true, :return_headers => true, :header_converters => :symbol, :converters => :all} )

Pour vérifier les données, cela fonctionne:

puts tickers[1][:ticker]
ZUMZ

Cependant ce n'est pas le cas:

puts tickers[:ticker => "XTEX"][:price]

Comment pourrais-je transformer ce tableau en un hachage en utilisant le champ ticker comme clé unique, de sorte que je pourrais facilement rechercher n'importe quel autre champ associativement tel que défini dans la ligne 1 de l'entrée? Traiter avec beaucoup plus de colonnes et de lignes.

Beaucoup apprécié!

27
demandé sur Marcos 2011-12-12 19:35:15

6 réponses

Comme ceci (cela fonctionne aussi avec d'autres CSV, pas seulement celui que vous avez spécifié):

require 'csv'

tickers = {}

CSV.foreach("stocks.csv", :headers => true, :header_converters => :symbol, :converters => :all) do |row|
  tickers[row.fields[0]] = Hash[row.headers[1..-1].zip(row.fields[1..-1])]
end

Résultat:

{"ZUMZ"=>{:price=>30.0, :market_cap=>933.9}, "XTEX"=>{:price=>16.02, :market_cap=>811.57}, "AAC"=>{:price=>9.83, :market_cap=>80.02}}

Vous pouvez accéder aux éléments de cette structure de données comme ceci:

puts tickers["XTEX"][:price] #=> 16.02

Edit( selon le commentaire): pour sélectionner des éléments, vous pouvez faire quelque chose comme

 tickers.select { |ticker, vals| vals[:price] > 10.0 }
33
répondu Michael Kohl 2011-12-12 16:34:25
CSV.read(file_path, headers:true, header_converters: :symbol, converters: :all).collect do |row|
  Hash[row.collect { |c,r| [c,r] }]
end
4
répondu Mr. Demetrius Michael 2014-12-16 00:12:16

Pour ajouter à la réponse de Michael Kohl, si vous voulez accéder aux éléments de la manière suivante

puts tickers[:price]["XTEX"] #=> 16.02

, Vous pouvez essayer l'extrait de code suivant:

CSV.foreach("Workbook1.csv", :headers => true, :header_converters => :symbol, :converters => :all) do |row|
    hash_row =  row.headers[1..-1].zip( (Array.new(row.fields.length-1, row.fields[0]).zip(row.fields[1..-1])) ).to_h
    hash_row.each{|key, value| tickers[key] ? tickers[key].merge!([value].to_h) : tickers[key] = [value].to_h}
end
1
répondu clouddra 2015-03-16 10:08:18

Pour obtenir le meilleur des deux mondes (lecture très rapide à partir D'un énorme fichier et les avantages d'un objet CSV Ruby natif), mon code a depuis évolué vers cette méthode:

$stock="XTEX"
csv_data = CSV.parse IO.read(%`|sed -n "1p; /^#{$stock},/p" stocks.csv`), {:headers => true, :return_headers => false, :header_converters => :symbol, :converters => :all}

# Now the 1-row CSV object is ready for use, eg:
$company = csv_data[:company][0]
$volatility_month = csv_data[:volatility_month][0].to_f
$sector = csv_data[:sector][0]
$industry = csv_data[:industry][0]
$rsi14d = csv_data[:relative_strength_index_14][0].to_f

Qui est plus proche de ma méthode d'origine, mais ne lit que dans un enregistrement plus la ligne 1 du fichier csv d'entrée contenant les en-têtes. Les instructions inline sed s'en occupent-et le tout est remarquablement instantané. C'est mieux que last parce que maintenant je peux accéder à tous les champs de Ruby, et associativement, ne se souciant plus des numéros de colonne comme ce fut le cas avec awk.

0
répondu Marcos 2017-05-23 12:33:14

Pas comme 1-liner-ie mais c'était plus clair pour moi.

csv_headers = CSV.parse(STDIN.gets)
csv = CSV.new(STDIN)

kick_list = []
csv.each_with_index do |row, i|
  row_hash = {}
  row.each_with_index do |field, j|
    row_hash[csv_headers[0][j]] = field
  end
  kick_list << row_hash
end
0
répondu Jesse Smith 2013-08-02 01:45:45

Bien que ce ne soit pas une solution Ruby native à 100% à la question initiale, si d'autres trébuchent ici et se demandent quel appel awk j'ai fini par utiliser pour l'instant, le voici:

$dividend_yield = IO.readlines("|awk -F, '$1==\"#{$stock}\" {print $9}' datafile.csv")[0].to_f

Où $ stock est la variable que j'avais précédemment assignée au symbole ticker d'une entreprise (le champ Clé wannabe). Survit commodément aux problèmes en retournant 0.0 si: ticker ou fichier ou champ # 9 introuvable / vide, ou si la valeur ne peut pas être typecastée à un float. Donc, tout ' % ' final dans mon cas devient bien tronquer.

Notez qu'à ce stade, on pourrait facilement ajouter plus de filtres dans awk pour avoir des e / s.readlines renvoie un tableau 1-dim de lignes de sortie du CSV résultant plus petit, par exemple.

 awk -F, '$9 >= 2.01  &&  $2 > 99.99  {print $0}' datafile.csv 

Sorties dans bash dont les lignes ont un DivYld (col 9) sur 2.01 et le prix (col 2) sur 99.99. (Malheureusement, je n'utilise pas la ligne d'en-tête pour déterminer les numéros de champ, ce qui est l'endroit où j'espérais finalement un tableau Ruby associatif consultable.)

-1
répondu Marcos 2011-12-14 18:10:44