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}