如何将哈希表保存到CSV文件中。

46

我刚开始学习 Ruby,请原谅我的新手问题。

我有一个包含两列的 CSV 文件,一列是动物名称,另一列是动物类型。我有一个哈希表,所有键都是动物名称,值是动物类型。我想将这个哈希表写入 CSV 文件,但不想使用 FasterCSV 库。我想到了几个实现方法,以下是基本的布局。

require "csv"

def write_file
  h = { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }

  CSV.open("data.csv", "wb") do |csv|
    csv << [???????????]
  end
end

我打开文件进行读取时,是使用这个命令打开的:File.open("blabla.csv", headers: true),是否可能以同样的方式将数据写回文件中?


3
你需要知道的是,Ruby 1.9用FasterCSV替换了旧的CSV模块,所以你实际上在使用FasterCSV。由于它是标准库的一部分,因此被称为CSV而不是FasterCSV。 - Paul Hoffer
8个回答

69

如果您想要列标题并且有多个哈希表:

require 'csv'
hashes = [{'a' => 'aaaa', 'b' => 'bbbb'}]
column_names = hashes.first.keys
s=CSV.generate do |csv|
  csv << column_names
  hashes.each do |x|
    csv << x.values
  end
end
File.write('the_file.csv', s)

(测试于 Ruby 1.9.3-p429)


其他答案对我都不起作用,因为它们无法保存列标题。这个答案完全可行。 - Nuno Costa
1
如果您不想实际输出到磁盘文件,那么CSV.generate非常方便。 - Ben Hull
随着行数的增加,生成操作在性能上与文件写入相比如何? - s2t2

44

试一下这个:

require 'csv'
h = { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }
CSV.open("data.csv", "wb") {|csv| h.to_a.each {|elem| csv << elem} }

结果将会是:

1.9.2-p290:~$ cat data.csv 
dog,canine
cat,feline
donkey,asinine

是的,那就是我的想法。将它转换回数组..非常酷,谢谢!并且使用块来完成它还加了5分!!击掌庆祝! :) - legendary_rob

33

我认为对于你最初的问题,最简单的解决方案是:

def write_file
  h = { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }

  CSV.open("data.csv", "w", headers: h.keys) do |csv|
    csv << h.values
  end
end

具有多个哈希,所有哈希均共享相同的密钥

def write_file
  hashes = [ { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' },
             { 'dog' => 'rover', 'cat' => 'kitty', 'donkey' => 'ass' } ]

  CSV.open("data.csv", "w", headers: hashes.first.keys) do |csv|
    hashes.each do |h|
      csv << h.values
    end
  end
end

6
在Ruby v2.5.3中,至少在CSV.open中需要添加write_headers: true参数来写入表头。 - go2null

29

CSV可以按任意顺序接收哈希,排除元素,并省略不在HEADERS中的参数。

require "csv"
HEADERS = [
  'dog',
  'cat',
  'donkey'
]

def write_file

  CSV.open("data.csv", "wb", :headers => HEADERS, :write_headers => true) do |csv|
    csv << { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }
    csv << { 'dog' => 'canine'}
    csv << { 'cat' => 'feline', 'dog' => 'canine', 'donkey' => 'asinine' }
    csv << { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine', 'header not provided in the options to #open' => 'not included in output' }
  end
end

write_file # => 
# dog,cat,donkey
# canine,feline,asinine
# canine,,
# canine,feline,asinine
# canine,feline,asinine

这使得使用CSV类更具灵活性和可读性。


4

我尝试了这里提供的解决方法,但由于我的源文件是LDIF文件,它并不总是具有某个键的所有值,因此我得到了一个不正确的结果(值在错误的列中)。最终,我使用了以下方法。

首先,在构建哈希表时,我记住了在一个单独的数组中的键,我会将未出现过的键添加到其中。

# building up the array of hashes
File.read(ARGV[0]).each_line do |lijn|
    case
    when lijn[0..2] == "dn:" # new record
        record = {}
    when lijn.chomp == '' # end record
        if record['telephonenumber'] # valid record ?
            hashes << record
            keys = keys.concat(record.keys).uniq
        end
    when ...
    end
end

这里最重要的一行是keys = keys.concat(record.keys).uniq,它在发现新键(标题)时扩展了键数组。

现在最重要的是将我们的哈希值转换为 CSV 格式。

CSV.open("export.csv", "w", {headers: keys, col_sep: ";"}) do |row|
  row << keys # add the headers
  hashes.each do |hash|
    row << hash # the whole hash, not just the array of values
  end
end

3

[谨慎] 此线程中的所有答案都假定哈希中定义的键的顺序在所有行中都是恒定的。

为了防止出现问题(就像我现在面临的问题一样),其中某些值被分配给CSV中的错误键(例如:)

hahes = [
    {:cola => "hello", :colb => "bye"},
    {:colb => "bye", :cola => "hello"}
]

使用此帖子中大多数回答(包括最佳答案)的代码生成以下表格:
cola  | colb
-------------
hello | bye
-------------
bye   | hello

你应该这样做:
require "csv"

csv_rows = [
    {:cola => "hello", :colb => "bye"},
    {:colb => "bye", :cola => "hello"}
]

column_names = csv_rows.first.keys

s=CSV.generate do |csv|
  csv << column_names
  csv_rows.each do |row|
    csv << column_names.map{|column_name| row[column_name]} #To be explicit
  end
end


1
让我们假设有一个哈希表,
hash_1 = {1=>{:rev=>400, :d_odr=>3}, 2=>{:rev=>4003, :d_price=>300}}

上面的hash_1以一些id 1,2..作为键,并将这些值再次哈希为一些键,如(:rev,:d_odr,:d_price)。 假设我们想要一个具有标题的CSV文件,请参见:
headers = ['Designer_id','Revenue','Discount_price','Impression','Designer ODR']

然后为hash_1的每个值创建一个新的数组,并将其插入到CSV文件中,

CSV.open("design_performance_data_temp.csv", "w") do |csv|
 csv << headers
 csv_data = []
 result.each do |design_data|
  csv_data << design_data.first
  csv_data << design_data.second[:rev] || 0
  csv_data << design_data.second[:d_price] || 0
  csv_data << design_data.second[:imp] || 0
  csv_data << design_data.second[:d_odr] || 0
  csv << csv_data
  csv_data = []
 end
end

现在您已经在相应的目录中保存了design_performance_data_temp.csv文件。上述代码可以进一步优化。

1

试试这个:

require 'csv'
data = { 'one' => '1', 'two' => '2', 'three' => '3' }

CSV.open("data.csv", "a+") do |csv|
        csv << data.keys
        csv << data.values
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接