我有一个摘要,我想为每个建议计数“是”和“否”:
https://images.homedepot-static.com/productImages/e0b36c9f-48c2-4fbd-ace8-3fa0e877aacc/svn/york-wallcoverings-wallpaper-at7053-64_1000.jpg
如何检索列的内容?
url.openStream())
所以我从这样的事情开始:
import java.net.URL
答案 0 :(得分:1)
这不是一个非常优雅的解决方案,但是是以“经典”(迭代编程)的方式完成的。我除去了Rails的内容,因此它是一个独立的应用程序。您必须将Rails-Stuff重新放回去。
请注意,您的CSV示例使用“ |” (管道)作为分隔符。
require 'csv'
# Start with 0 of each
counts = {cats: 0, dogs: 0, rabbits: 0}
# run over every row
CSV.foreach("c.csv", headers: true, col_sep: "|") do |row|
# Check answers in each column and increase count if "Yes"
if row[1].strip == 'Yes'
counts[:cats] = counts[:cats] + 1
end
if row[2].strip == 'Yes'
counts[:dogs] = counts[:dogs] + 1
end
if row[3].strip == 'Yes'
counts[:rabbits] = counts[:rabbits] + 1
end
end
puts counts # Will print {:cats=>2, :dogs=>1, :rabbits=>2}
请注意,可以多种方式极大地简化CSV-Access /使CSV-Access更具可读性(请参见https://ruby-doc.org/stdlib-2.0.0/libdoc/csv/rdoc/CSV.html)。通过使用Enumerable模块https://ruby-doc.org/core-2.6.2/Enumerable.html中的方法,可以大大简化和美化计数和分组,在示例代码中是通过手动遍历每一行和每一列来完成的。阅读和理解给定的链接将大大提高您的编程性能。
有趣的学习和黑客活动!
答案 1 :(得分:1)
csv =<<-END
Name | Cats| Dogs| Rabbits|
john | Yes | No | No |
max | No | No | Yes |
oli | Yes | Yes | Yes |
END
FNAME = 'temp.csv'
File.write(FNAME, csv)
#=> 109
使用CSV方法
我们可以如下使用CSV方法。
require 'csv'
csv = CSV.read(FNAME, headers: true, col_sep: '|')
csv.headers.each_with_object({}) do |animal,h|
unless animal.nil? || animal.strip == "Name"
h[animal.strip.downcase] = csv[animal].count { |s| s.strip == "Yes" }
end
end
#=> {"cats"=>2, "dogs"=>1, "rabbits"=>2}
在预处理文件后使用CSV
方法
在这里使用CSV
方法有点麻烦。对于一个:
csv.headers
#=> ["Name ", " Cats", " Dogs", " Rabbits", nil]
存在元素nil
的原因是分隔符为|
,并且该字符出现在每行的末尾。第二个问题是空间的存在。例如,如果列标签" Cats"
是"Cats"
,或者更好的是"cats"
,则会更方便。
鉴于这些复杂性,人们可能会考虑对文件进行一些简单的预处理,以使其更易于应用CSV
方法。
TEMP_FNAME = 'temp1.csv'
File.write(TEMP_FNAME, File.read(FNAME).delete(' ').downcase.gsub(/\|$/,''))
#=> 68
让我们看看所写的内容。
puts File.read(TEMP_FNAME)
name|cats|dogs|rabbits
john|yes|no|no
max|no|no|yes
oli|yes|yes|yes
我们现在可以很容易地构造所需的哈希。
csv = CSV.read(TEMP_FNAME, headers: true, col_sep: '|')
csv.headers.each_with_object({}) do |animal,h|
h[animal] = csv[animal].count("yes") unless animal == 'name'
end
#=> {"cats"=>2, "dogs"=>1, "rabbits"=>2}
可以说,分两个步骤进行操作还可以简化调试和测试。
将文件视为普通文本文件
当文件的内容不允许直接使用CSV
方法时,将文件视为普通文本文件可能更简单:
File.read(FNAME).downcase.split("\n").
map { |line| line.split(/ *\| */)[1..] }.transpose.
each_with_object({}) { |(lbl,*rest),h| h[lbl]=rest.count('yes') }
#=> {"cats"=>2, "dogs"=>1, "rabbits"=>2}
步骤如下。
a = File.read(FNAME).downcase.split("\n")
puts a
name | cats| dogs| rabbits|
john | yes | no | no |
max | no | no | yes |
oli | yes | yes | yes |
b = a.map { |line| line.split(/ *\| */)[1..] }
#=> [["cats", "dogs", "rabbits"],
# ["yes", "no", "no"],
# ["no", "no", "yes"],
# ["yes", "yes", "yes"]]
c = b.transpose
#=> [["cats", "yes", "no", "yes"],
# ["dogs", "no", "no", "yes"],
# ["rabbits", "no", "yes", "yes"]]
c.each_with_object({}) { |(lbl,*rest),h| h[lbl]=rest.count('yes') }
#=> {"cats"=>2, "dogs"=>1, "rabbits"=>2}
答案 2 :(得分:0)
这可以做到:
CSV.open("summary.csv", col_sep: ";")
.to_a
.transpose[1..]
.map { |(name, *data)| [name, data.count { |val| val == "Yes" }] }
.to_h
其输出:
{
"Cats" => 2,
"Dogs" => 1,
"Rabbits" => 2
}
分步说明:
首先,让我们阅读数据:
irb> CSV.open("x.csv", col_sep: ";").to_a
=> [["Name", "Cats", "Dogs", "Rabbits"], ["john", "Yes", "No", "No"], ["max", "No", "No", "Yes"], ["oli", "Yes", "Yes", "Yes"]]
接下来,转置以将动物和价值观放在一起:
irb> CSV.open("x.csv", col_sep: ";").to_a.transpose
=> [["Name", "john", "max", "oli"], ["Cats", "Yes", "No", "Yes"], ["Dogs", "No", "No", "Yes"], ["Rabbits", "No", "Yes", "Yes"]]
请注意第一个元素是不相关的,因此我们通过附加[1..]
以获取除第一个元素以外的所有元素来忽略它。
然后,我们只需要将每个数组元素转换为动物名和"Yes"
值的计数即可。对于单个元素,我们可以执行以下操作:
row = ["Cats", "Yes", "No", "Yes"]
(name, *data) = row
name
将是"Cats"
,而data
将包含其他元素,即它将是["Yes", "No", "Yes"]
。
现在,我们只需要计算"Yes"
中的data
值:
irb> [name, data.count { |val| val == "Yes" }]
=> ["Cats", 2]
使用map
,我们可以对转置数据集的所有元素执行此操作。调用#to_h
会将每个数组的第一个元素用作键,将第二个元素用作值。
答案 3 :(得分:0)
首先将数据结算到table
中:
require 'csv'
raw_table = File.read("summary.csv")
table = CSV.parse(raw_table.gsub(' ', ''), col_sep: '|', headers: true)
这会将数据读入字符串,删除空格并使用分隔符和标头解析为CSV。
列分隔符为'|'
,因为最右端有一个分隔符,它会生成一个空列。然后需要将其删除,请参阅下面在此处使用的.compact
或[1...-1]
。
h_table = table.map { |e| e.to_h.compact }
#=> [{"Name"=>"John", "Cats"=>"Yes", "Dogs"=>"No", "Rabbits"=>"No"}, {"Name"=>"Max", "Cats"=>"No", "Dogs"=>"No", "Rabbits"=>"Yes"}, {"Name"=>"Oli", "Cats"=>"Yes", "Dogs"=>"Yes", "Rabbits"=>"Yes"}]
将counts
设置为零,然后扫描h_table
:
counts = h_table.first.transform_values { |v| 0 }.tap{ |h| h.delete 'Name'}
counts #=> {"Cats"=>0, "Dogs"=>0, "Rabbits"=>0}
counts.keys.each { |k| h_table.each { |h| counts[k] += 1 if h[k] == 'Yes' } }
counts #=> {"Cats"=>2, "Dogs"=>1, "Rabbits"=>2}
table.to_a.transpose[1...-1].each_with_object({}) { |(k, *v), h| h[k] = v.count{ |e| e == 'Yes' } }
#=> {"Cats"=>2, "Dogs"=>1, "Rabbits"=>2}
Name;Cats;Dogs;Rabbits
John;Yes;No ;No
Max;No ;No ;Yes
Oli;Yes;Yes;Yes
只需使用table = CSV.read("summary.csv", col_sep: ';', headers: true)
然后应用上述方法之一,只需不删除空列即可。