-1

I'm currently working with a .csv file in ruby and I'm trying to figure out how to count the number of items that are over 60 in the file.

I've imported the csv file into a hash and counted the total number of items, but I want to know how to count the number of items over 60 now.

This is an example of what I'm looking for

csv.csv:

55, 56
60, 61
63, 9
3, 62

My code so far:

myhash=[]
myhash1=[]
total=0
count=0
file=File.open('csv.csv',"r")

???

puts count 

Expected output: 4

Tom Lord
  • 27,404
  • 4
  • 50
  • 77
  • "I've imported the csv file into a hash" Do you mean you've imported the CSV file into an *array* of hashes? Otherwise you're going to need to be more specific about what format your data is in. Also, what do you mean by "number of items that are over 60"? Do you mean the number of rows have a value greater than 60 in a particular column? Please edit your question to include an example of the data you're working with and the desired output. – Jordan Running May 30 '17 at 21:32
  • Just edited it. – Confused_Student May 30 '17 at 21:41
  • Your CSV file only has one row? Is it all numbers? – Jordan Running May 30 '17 at 21:42
  • It's all numbers ,but it's like several pages long and I'm just giving an example of what's in the csv file. – Confused_Student May 30 '17 at 21:45
  • 1
    Several pages... of *one line*? Or multiple lines? – Tom Lord May 30 '17 at 21:45
  • multiple lines with two items on each line. My enter key makes my computer blue screen so I couldn't make it look like the csv. It's several pages of numbers like that. – Confused_Student May 30 '17 at 21:47
  • OK... I'll edit your *example* above to actually look like your CSV file. It's not a very good example if it's in a different format. – Tom Lord May 30 '17 at 21:49
  • Ok. Yeah sorry about that. I need to buy a new keyboard or something. I think its a short causing me to blue screen when I enter. – Confused_Student May 30 '17 at 21:53
  • Your question isn't asked well. Please read "[mcve]". We need to see what you _tried_, not just a general framework wrapping what you'd try. See "[How much research effort is expected of Stack Overflow users?](http://meta.stackoverflow.com/questions/261592)" and "[Writing the perfect question](https://codeblog.jonskeet.uk/2010/08/29/writing-the-perfect-question/)". Without that little evidence of effort it looks like you didn't try and want us to write it for you. – the Tin Man May 30 '17 at 22:05
  • You say you want `4` as a result, but the requirement is for values _over_ 60. There are only three that are `> 60`. – the Tin Man May 30 '17 at 22:37

4 Answers4

3

There are multiple ways to solve this. Here's one approach that does the whole operation in one go, without even looping through each line:

File
  .read('csv.csv') # Read the file into a ruby String
  .split(/,|\n/)   # Convert into an Array of each string (splitting on commas OR newlines) (e.g. ['55', '56', '60', ... ])
  .map(&:to_i)     # Convert into an Array of each integer (e.g. [55, 56, 60, ... ])
  .count { |n| n >= 60 }

Here's a similar approach, that instead loops through each line and adds the count to a variable. Using foreach like this is especially better if you have a very large file - as you won't be loading it all into memory at once:

total = 0
File.foreach('csv.csv') do |line|
  total += line.split(',').count {|n| n.to_i >= 60 }
end

And here's another approach that actually uses ruby's CSV library, so you don't need to explicitly split on the commas:

require 'csv'

total = 0
CSV.foreach('csv.csv') do |row|
  total += row.count {|n| n.to_i >= 60 }
end
Tom Lord
  • 27,404
  • 4
  • 50
  • 77
  • Because CSV files tend to be large, especially when used to transfer data between databases, using `read` isn't a good idea as it can kill a running script. `foreach` is a much better solution. https://stackoverflow.com/questions/25189262 – the Tin Man May 30 '17 at 22:08
  • I got a zero when I tried using file.count I'm not sure what I did wrong. – Confused_Student May 30 '17 at 22:14
  • 1
    If you want us to explain *what you did wrong*, then you needed to show us *what you did*! – Tom Lord May 30 '17 at 22:15
0

While I usually caution against using naïve means to deal with CSV data, if indeed your CSV file is all numbers, then I think using the CSV module is overkill. You can use File.foreach to iterate over the file's lines and String#scan to iterate over the numbers in the file.

n = 0
File.foreach("csv.csv") do |line|
  line.scan(/\d+/) {|s| num += 1 if s.to_i >= 60 }
end

p n # => 4

This method has the benefit of not reading the entire file into memory at once, nor creating large intermediate arrays.

You can see it (almost) in action on repl.it: https://repl.it/IY5X (It's different only because you can't read from files on repl.it.)

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
  • @tadman `csv.rb` is 2.4kloc (including comments), and using it would not appreciably reduce the amount of code OP would need to write. Even if using the CSV module only saved OP from writing three lines of code I'd probably say they should use it. In this case it doesn't even save one line of code, so why bother? – Jordan Running May 30 '17 at 22:06
  • 1
    2.4KLOC is nothing these days, you should know that. It's a good tool, it's bundled with Ruby so it's dependably there, and it *works*. I don't see how `scan` is any better here, especially if this CSV file gets more ornate in later iterations. – tadman May 30 '17 at 22:19
0

The shortest way I know of to do this is:

File.read('foo.csv').scan(/\d+/).select { |d| d.to_i > 60 ).size

Don't forget about Enumerable#count

Good point:

DATA.read.scan(/\d+/).count { |s| s.to_i > 60 }  # => 3

The downside to these is that they're not scalable. See "Why is "slurping" a file not a good practice?" for more information.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
0

If the values are all treated equally:

require 'csv'

CSV.read('input.csv').flatten.map(&:to_i).count { |i| i >= 60 }
# => 4
tadman
  • 208,517
  • 23
  • 234
  • 262