【问题标题】:(Ruby) How to convert a nested hash into a csv file(Ruby)如何将嵌套哈希转换为 csv 文件
【发布时间】:2019-08-01 23:03:11
【问题描述】:

问题:

我有以下哈希:

hash = {1=>{:price=>"400", :brand=>"Primark"}, 2=>{:price=>"1000", :brand=>"pull&bear"}, 3=>{:price=>"1700", :brand=>""}, 4=>{:price=>"500", :brand=>"H&M"}, 5=>{:price=>"500", :brand=>“芒果”}}

我想把它转换成 csv 文件。


目前的方法:

我最接近的解决方案(inspired by) 似乎不断地覆盖自己:

require 'csv'

(1..5).each do |id|
   column_names = hash[id].keys
   column_values = hash[id].values
   s=CSV.generate do |csv|
      csv << column_names
      csv << column_values
   end
   File.write('the_file.csv', s)
end

当前 csv 文件:

+---+------------+-------------+
|   |     A      |      B      |
+---+------------+-------------+
| 1 | price,brand|             |
| 2 | 500, Mango |             |
+---+------------+-------------+

所需的 csv 输出:

+---+------------+-------------+
|   |     A      |      B      |
+---+------------+-------------+
| 1 | price      | brand       |
| 2 | 400        | Primark     |
| 3 | 1000       | pull&bear   |
| 4 | 1700       |             |
| 5 | 500        | H&M         |
| 6 | 500        | mango       |
+---+------------+-------------+

这里有几个问题可以处理逆向(将 csv 转换为嵌套哈希)或仅将简单哈希转换为 csv 文件,而不是嵌套哈希。我对 Ruby 还很陌生,还不能把这些点联系起来。非常感谢您的帮助!

【问题讨论】:

  • 说出您想要的输出并显示您实际获得的输出会非常有用。
  • 我添加了所需/当前的输出,谢谢!
  • 请注意,迄今为止的两个解决方案都使用了Hash#values_at 方法。如果不明显,则使用它以便不需要对哈希键的顺序进行假设(:price:brand)。

标签: ruby csv export-to-csv


【解决方案1】:

您似乎只对嵌套哈希的值感兴趣。在这种情况下,你可以简单地做

titles = hash.values.map(&:keys).flatten.uniq
rows = hash.values.map { |data| data.values_at(*titles) }

s = CSV.generate do |csv|
  csv << titles
  rows.each do |row|
    csv << row
  end
end

(更新)

标题应该被展平为一个简单的数组。

【讨论】:

  • 感谢您的解决方案!不幸的是,它不起作用。以下是终端输出:irb(main):170:0&gt; titles = hash.values.map(&amp;:keys).uniq =&gt; [[:price, :brand]]irb(main):171:0&gt; rows = hash.values.map { |data| data.values_at(*titles) } =&gt; [[nil], [nil], [nil], [nil], [nil]]irb(main):172:0&gt; s = CSV.generate do |csv| irb(main):173:1* csv &lt;&lt; titles irb(main):174:1&gt; rows.each do |row| irb(main):175:2* csv &lt;&lt; row irb(main):176:2&gt; end irb(main):177:1&gt; end =&gt; "\"[:price, :brand]\"\n\n\n\n\n\n" 导出的 csv 文件在一行中仅包含“[:price, :brand]”
  • 是的,我忘了展平标题列表。标题应该是 [:price, :brand] 而不是 [[:price, :brand]]
【解决方案2】:

我会这样做:

require 'csv'

CSV.open('the_file.csv', 'w') do |csv|
  hash.each do |id, attributes|    
    csv << [id, *attributes.values_at(:price, :brand)]
  end
end

【讨论】:

  • 谢谢你,斯派克曼!您的解决方案工作正常。包括@Grzegorz 代码中的headers = hash.values[0].keyscsv &lt;&lt; headers 会添加标题。
【解决方案3】:

CSV 格式本质上是一个数组数组。主数组的每个元素都是单行。单行是一个单元格数组。所以你想要的基本上是这样的:

[
  [:price, :brand],
  ["400", "Primark"],
  ["1000", "Pull&Bear"]
]

您可以通过以下方式实现:

headers = hash.values[0].keys # this will return [:price, :brand] and we'll use it as a header
data = hash.values.map(&:values)

csv_output = CSV.generate do |csv|
  csv << headers # we need headers only once, we don't need them in every row
  data.each do |single_row| # iterate over each row
    csv << single_row # add the row to csv file
  end
end
File.write('the_file.csv', csv_output)

此代码假定您将获得每一行的所有可用数据(即,每一行都将提供价格和品牌)。上面用户提供的代码重写后更加灵活。

【讨论】:

  • 谢谢!您的解决方案准确返回所需的输出,包括标题。
猜你喜欢
  • 2020-12-22
  • 2016-09-14
  • 1970-01-01
  • 2015-08-12
  • 2013-11-13
  • 1970-01-01
  • 2020-01-30
  • 2011-05-30
  • 1970-01-01
相关资源
最近更新 更多