从类似CSV的文件中创建哈希表

3

我有一个产品文件,列出了商品编号、产品和价格。我想要读取这个文件,并将其初始化为哈希表,其中商品编号是键,产品和价格是值。以下是我的文件:

199, Shoes, 59.99
211, Shirts, 19.99
245, Hats, 25.99
689, Coats, 99.99
712, Beanies, 6.99

我希望它看起来像这样。

products = {
  199 =>['Shoes', 59.99],
  211 =>['Shirts', 19.99],
  245 =>['Hats', 25.99],
  689 => ['Coats', 99.99],
  712 => ['Beanies', 6.99]
}

这是我能想到的,但并不是我想要的内容。
products_file = File.open("files.txt")
products_hash = []
while ! products_file.eof?
  product_hash = products_file.gets.chomp
  print product_hash.split(', ')
end

以下是我得出的输出结果:

["199", "Shoes", "59.99"]
["211", "Shirts", "19.99"]
["245", "Hats", "25.99"]
["689", "Coats", "99.99"]
["712", "Beanies", "6.99"]
2个回答

11

我将你的数据保存为名为products.csv的CSV文件,并执行了以下操作:

require 'csv'
products = {}
CSV.foreach("products.csv") do |line| 
  products[line[0].to_i] = [line[1].strip, line[2].to_f]
end
products
#=> {199=>["Shoes", 59.99], 211=>["Shirts", 19.99], 245=>["Hats", 25.99], 689=>["Coats", 99.99], 712=>["Beanies", 6.99]}

使用each_with_object可以更简洁地实现相同的结果,但它会一次性将整个文件读入内存,如果文件很大,则可能不是一个好主意:

require 'csv'
products = CSV.read("products.csv").each_with_object({}) do |line, h|
  h[line[0].to_i] = [line[1].strip, line[2].to_f]
end 

还有一种更加函数式的方法,正如Phrogz最初建议的那样:

require 'csv'
products = Hash[ CSV.read('products.csv').map do |row|
  [ row[0].to_i, [row[1].strip,row[2].to_f] ]
end ]

+1,好答案。對於這個話題,我一直重複強調,但是我支持使用 tap 而不是 each_with_object此連結提供了詳細的解釋。 - Phrogz
我的问题在于我更喜欢在要转换的数据上调用方法,而不是在即将成为结果的空集合上调用方法。然而,我认为对于这种情况,“into”或“fill_with”将是一个不错的别名,可以稍微缓解我的担忧。个人而言,我总是使用“inject”,但发现新一代的Ruby程序员更容易理解“each_with_object”。 - Michael Kohl

1
一种变化,使用CSV的转换器来操作数据:
require 'csv'
products = {}
CSV.foreach('products.csv', {col_sep: ', ', converters: :numeric}) do |row|
  products[row.shift] = row
end
p products
#=> {199=>["Shoes", 59.99], 211=>["Shirts", 19.99], 245=>["Hats", 25.99], 689=>["Coats", 99.99], 712=>["Beanies", 6.99]}

+1,不错的解决方案。我的初衷是让代码简单并展示一些 Ruby 的特性,但我认为你的代码也做到了这两点。 - Michael Kohl

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