ruby - tab from row not respected - ruby

I'm reading a simple txt file very well. However i'm getting the data row with tab not respected.
Below the row in the file.
Anderson Silva R$10 off R$20 of food 10.0 2 987 Fake St Batman Inc
And below is the out line at pry.
As we can see the 987 and Fake St is together in the same row.
Anderson Silva
R$10 off R$20 of food
10.0
2
987 Fake St
Batman Inc
and here the simple code
line.split("\t").map do |col|
col = col.split("\t")
puts col
end

I don't know if I'm understanding your question correctly, but I'd suspect that there's not actually a tab where you expect one.
def contrived_method(str)
str.split("\t").each do |col|
col = col.split("\t")
puts col
end
end
line1 = "10.0\t2\t987 Fake St"
line2 = "10.0\t2\t987\tFake St"
contrived_method(line1)
#=> 10.0
#=> 2
#=> 987 Fake St
contrived_method(line2)
#=> 10.0
#=> 2
#=> 987
#=> Fake St
For demonstration, I've reduced the size of your string to show that the String::split method will indeed split on the supplied delimiter. And--in this case--I've used eachinstead of mapbecause there's no assignment.
You'll find the inspect method valuable in this case:
line1 = "10.0\t2\t987 Fake St"
puts line1.inspect
#=> "10.0\t2\t987 Fake St"
puts line1
#=> 10.0 2 987 Fake St

Related

How do I count unique multiple words in a Ruby string?

Trying to write a Ruby code that will count unique words and return their total occurrences.
So suppose I want to find number of occurrences for Sally, Marina and Tina in the following sentence "Monday Tina will meet Sally and Harris. Then Tina will visit her mom Marina. Marina and Tina will meet David for dinner."
I tried the following but this defeats the dry principal. Is there a better way?
string = "Monday Tina will meet Sally and Harris. Then Tina will visit her mom Marina. Marina and Tina will meet David for dinner. Sally will then take Tina out for a late night party."
puts "Marina appears #{string.split.count("brown").to_i} times."
puts "Tina appears #{string.split.count("grey").to_i} times."
puts "Sally appears #{string.split.count("blue").to_i} times."
Expected result: program looks through the text for unique words and returns them.
Actual: I had to hard code each unique word on its own PUTS line and do string.split.count(for that unique word)
Note:
I tried the following but this gives me EVERY word. I need to refine it to give me just the ones I ask for. This is where I am struggling.
def cw(string)
w = string.split(' ')
freq = Hash.new(0)
w.each { |w| freq[w.downcase] += 1 }
return freq
end
puts cw(string)
def count_em(str, who)
str.gsub(/\b(?:#{who.join('|')})\b/i).
each_with_object(Hash.new(0)) { |person,h| h[person] += 1 }
end
str = "Monday Tina will meet Sally and Harris. Then Tina will visit her " +
"mom Marina. Marina and Tina will meet David for dinner. Sally will " +
"then take Tina out for a late night party."
who = %w| Sally Marina Tina |
count_em(str, who)
#> {"Tina"=>4, "Sally"=>2, "Marina"=>2}
The first steps are as follows.
r = /\b(?:#{who.join('|')})\b/i
#=> /\b(?:Sally|Marina|Tina)\b/i
enum = str.gsub(r)
#=> #<Enumerator: "Monday Tina will meet Sally and Harris. Then
# ...
# for a late night party.":gsub(/\b(?:Sally|Marina|Tina)\b/i)>
We can convert this to an array to see the values that will be passed to each_with_object.
enum.to_a
#=> ["Tina", "Sally", "Tina", "Marina", "Marina", "Tina", "Sally", "Tina"]
We then simply count the number of instances of the unique values generated by enum.
enum.each_with_object(Hash.new(0)) { |person,h| h[person] += 1 }
#=> {"Tina"=>4, "Sally"=>2, "Marina"=>2}
See String#gsub, in particular the case when there is one argument and no block. This is admittedly an unusual use of gsub, as it is making no substitutions, but here I prefer it to String#scan because gsub returns an enumerator whereas scan produces a temporary array.
See also Hash::new, the case where new takes an argument and no block. The argument is called the default value. If h is the hash so-defined, the default value is returned by h[k] if h does not have a key k. The hash is not altered.
Here the default value is zero. When the expression h[person] += 1 it is parsed it is converted to:
h[person] = h[person] + 1
If person equals "Tina", and it is the first time "Tina" is generated by the enumerator and passed to the block, h will not have a key "Tina", so the expression becomes:
h["Tina"] = 0 + 1
as 0 is the default value. The next time "Tina" is passed to the block the hash has a key "Tina" (with value 1), so the following calculation is performed.
h["Tina"] = h["Tina"] + 1 #=> 1 + 1 #=> 2
To get only the required people name:
people = ['Marina', 'Tina', 'Sally', 'Dory']
tmp = string.scan(/\w+/).keep_if{ |w| people.include? w }
counts people.map{ |name| [name, tmp.count{|n| n == name }] }.to_h
counts #=> {"Marina"=>2, "Tina"=>4, "Sally"=>2, "Dory"=>0}
This maps the peopole array against tmp to a nested array containing [name, count], then converted to a hash.
The good is that it returns 0 if people doesn't appear, see 'Dory'.
To get the total count, two ways:
tmp.size #=> 8
counts.values.sum #=> 8

Ruby - Cannot sum up hash values

I'm new to Ruby (and programming in general). I have a hash that is using data from an external file, and I'm trying to get the total number of values that are greater than 1500.
Here's my code Actually, I need both the number of entries and the total value of purchase orders over 1500. The external file is just a column of order numbers and a column of prices. I'm sure there is a very simple solution, but like I said I'm a beginner and can't figure it out. Any help would be appreciated. Thanks.
Edit: Here is my code. It's just that last while loop that's causing all the problems. I know that's not the right way to go about it, but I just can't figure out what to do.
myhash={}
file=File.open("Purchase Orders.csv", "r")
while !file.eof
line=file.readline
key,value=line.chomp.split(",")
myhash[key]=value
end
total=0
entries=myhash.length
newtotal=0
myhash.each { |key,value|
total+=value.to_f
}
puts total
puts entries
while value.to_f>1500
myhash.each {|key,value| newtotal+=value.to_f}
end
puts newtotal
I will rewrite the code in ruby idiomatic way in hope you’ll examine it and find out some hints.
prices = File.readlines("Purchase Orders.csv").map do |line|
line.chomp.split(",").last.to_f
end # array of prices
total = prices.inject(:+) # sum values
pricy = prices.select { |v| v > 1500 }
pricy_sum = pricy.inject(:+) # sum expensives
pricy_count = pricy.length # expensives’ size
puts "Total sum is: #{total}"
puts "Total expensives is: #{pricy}"
looks like you have your loops reversed. Also, using do and end is usually preferred over curly braces for multiline code blocks, while curly braces are generally used for single line blocks (as noted by #mudasobwa). Check out the ruby style guide for some more style pointers.
myhash.each do |key,value|
newtotal+=value.to_f if value.to_f > 1500
end
puts newtotal
Code
def nbr_and_tot(fname)
File.foreach(fname).with_object({ nbr_over: 0, tot_over: 0 }) do |line, h|
n = line[/\d+/].to_i
if n > 1500
h[:nbr_over] += 1
h[:tot_over] += n
end
end
end
Example
First let's create a file "temp":
str =<<-END
:cat, 1501
:dog, 1500
:pig, 2000
END
File.write("temp", str)
#=> 33
Confirm the file is correct:
puts File.read("temp")
prints
:cat, 1501
:dog, 1500
:pig, 2000
Now execute the method.
nbr_and_tot "temp"
#=> {:nbr_over=>2, :tot_over=>3501}
Explanation
First review, as necessary, IO::foreach, which reads the file line-by-line1 and returns an enumerator that is chained to with_object, Enumerator#with_object and String#[].
For the example,
fname = "temp"
e0 = File.foreach(fname)
#=> #<Enumerator: File:foreach("temp")>
We can see the values that will be generated by this enumerator (and passed to each_object) by converting it to an array:
e0.to_a
#=> [":cat, 1501\n", ":dog, 1500\n", ":pig, 2000\n"]
Continuing,
e1 = e0.with_object({ nbr_over: 0, tot_over: 0 })
#=> #<Enumerator: #<Enumerator: 2.3.0 :171 >
e1.to_a
#=> [[":cat, 1501\n", {:nbr_over=>0, :tot_over=>0}],
# [":dog, 1500\n", {:nbr_over=>0, :tot_over=>0}],
# [":pig, 2000\n", {:nbr_over=>0, :tot_over=>0}]]
The first element generated by e1 is passed to the block and the block variables are assigned values, using parallel assignment:
line, h = e1.next
#=> [":cat, 1501\n", {:nbr_over=>0, :tot_over=>0}]
line
#=> ":cat, 1501\n"
h #=> {:nbr_over=>0, :tot_over=>0}
and n is computed:
s = line[/\d+/]
#=> "1501"
n = s.to_i
#=> 1501
As n > 1500 #=> true, we perform the following operations:
h[:nbr_over] += 1
#=> 1
h[:tot_over] += n
#=> 1501
so now
h #=> {:nbr_over=>1, :tot_over=>1501}
Now the second element of e1 is passed to the block and the following steps are performed:
line, h = e1.next
#=> [":dog, 1500\n", {:nbr_over=>1, :tot_over=>1501}]
line
#=> ":dog, 1500\n"
h #=> {:nbr_over=>1, :tot_over=>1501}
n = line[/\d+/].to_i
#=> 1500
As n > 1500 #=> fasle, this line is skipped. The processing of the last element generated by e1 is similar to that for the first element.
1 File is a subclass of IO (File < IO #=> true), so IO class methods such as foreach are often invoked on the File class (File.foreach...).

How to count 1 to 9 on a single line in ruby

I'm struggling to figure out how to loop numbers in a single line on ruby.
x = 0
while x <= 9
puts x
x +=1
end
This would give you
0
1
2
3
4
5
6
7
8
9
Each on different lines.
But what I want is to get this on a single line so like
01234567891011121314151617181920
Also not limited to just 0-9 more like 0 to infinity on a single line.
The purpose is to make an triangle of any size that follows this pattern.
1
12
123
1234
12345
123456
Each of these would be on a different line. The formatting here won't let me put in on different lines.
Would really like to solve this. It is hurting my head.
try this:
(1..9).each { |n| print n }
puts
You said you want "to make a triangle of any size that follows this pattern", so you should not make assumptions about how that should be done. Here are two ways to do that.
#1
def print_triangle(n)
(1..n).each.with_object('') { |i,s| puts s << i.to_s }
end
print_triangle(9)
1
12
123
1234
12345
123456
1234567
12345678
123456789
#2
def print_triangle(n)
s = (1..n).to_a.join
(1..n).each { |i| puts s[0,i] }
end
print_triangle(9)
1
12
123
1234
12345
123456
1234567
12345678
123456789
how about this solution:
last_num = 9
str = (1..last_num).to_a.join # create string 123456789
0.upto(last_num-1){ |i| puts str[0..i] } # print line by line
puts (1..9).map(&:to_s).join
Regarding your final aim there are lots of (probably easier) ways, but here's one:
def print_nums k
k.times { |n| puts (1..(n+1)).map { |i| i*10**(n+1-i) }.inject(:+) }
end
print_nums 9
#1
#12
#123
#1234
#12345
#123456
#1234567
#12345678
#123456789
This approach generates the actual numbers using units, tens, hundreds etc in relation to the line number i.
Thought Process
Looking at a basic example of four lines:
1
12
123
1234
is the same as:
1*10**0 #=> 1
1*10**1 + 2*10**0 #=> 12
1*10**2 + 2*10**1 + 3*10**0 #=> 123
1*10**3 + 2*10**2 + 3*10**1 + 4*10**0 #=> 1234
which in Ruby can be generated with:
(1..1).map { |i| i*10**(1-i) }.inject(:+) #=> 1
(1..2).map { |i| i*10**(2-i) }.inject(:+) #=> 12
(1..3).map { |i| i*10**(3-i) }.inject(:+) #=> 123
(1..4).map { |i| i*10**(4-i) }.inject(:+) #=> 1234
looking for a pattern we can generalise and put in a method:
def print_nums k
k.times { |n| puts (1..(n+1)).map { |i| i*10**(n+1-i) }.inject(:+) }
end
You could (and should) of course ignore all of the above and just extend the excellent answer by #seph
3.times { |i| (1..(i+1)).each { |n| print n }; puts }
#1
#12
#123
The simplest way if you want to start from 1
9.times {|n| puts n+1}
try if you want to start from 0
10.times {|n| puts n}
if you want pyramid format this is one way to do
9.times{|c| puts (1..c+1).to_a.join}
this is the ouput
2.3.0 :025 > 9.times{|c| puts (1..c+1).to_a.join}
1
12
123
1234
12345
123456
1234567
12345678
123456789

Entire hash getting returned at the end of the loop

Code
$dail_book = {
"los_angeles" => 212,
"new_york" => 523,
"portland" => 234,
"seattle" => 502,
"miami" => 910,
"san_francisco" => 345,
"sioux_falls" => 543,
"omaha" => 642,
"minneapolis" => 342,
"san_diego" => 233
}
# Removes the underscore, captalizes each city,
# and prints it back to the user
def format_cities(k)
puts "#{k.split('_').map(&:capitalize).join(' ')}"
end
# Loops through $dail_book
def display_cities
puts "Options: "
puts $dail_book.sort.each {|k,v| format_cities(k)}
end
Output
Options:
Los Angeles
Miami
Minneapolis
New York
Omaha
Portland
San Diego
San Francisco
Seattle
Sioux Falls
los_angeles
212
miami
910
minneapolis
342
new_york
523
omaha
642
portland
234
san_diego
233
san_francisco
345
seattle
502
sioux_falls
543
Question
Why does the entire hash get returned at the end of the loop? What's happening?
The each method returns the original enumerable object it was called upon, this is why you keep putsing the entire hash after the end of the loop.
You are calling puts twice in your code:
def format_cities(k)
puts ... # <- here
end
def display_cities
# ...
puts $dail_bo... # <- and here
end
Try to keep your methods focused and modular. display_cities obviously displays something, so puts is expected here. format_cities on the other hand converts a value. It should not print anything.
Futhermore, its name (..._cities, plural) suggest that it is formatting multiple cities at once, whereas it only formats one city at a time. It should therefore be called format_city (singular):
# removes the underscore, capitalizes each word
def format_city(city)
city.split('_').map(&:capitalize).join(' ')
end
Then, move the printing part into the display related method. But instead of printing the result of each (which returns the collection), move puts into the loop to print each formatted city name:
def display_cities
puts "Options: "
$dail_book.sort.each { |k, v| puts format_city(k) }
end

Applyng expression to ruby search and replace with regexp

I know in ruby you can search and replace a string using gsub and a regular expression.
However is there away to apply an expression to the result of the search and replace before it is replaced.
For example in the code below though you can use the matched string with \0 you cannot apply expressions to it (eg \0.to_i * 10) and doing so results in an error:
shopping_list = <<LIST
3 apples
500g flour
1 ham
LIST
new_list = shopping_list.gsub(/\d+/m, \0.to_i * 10)
puts new_list #syntax error, unexpected $undefined
It seems to only work with a string literal:
shopping_list = <<LIST
3 apples
500g flour
1 ham
LIST
new_list = shopping_list.gsub(/\d+/m, '\0 string and replace')
puts new_list
Is this what you want?
shopping_list = <<LIST
3 apples
500g flour
1 ham
LIST
new_list = shopping_list.gsub(/\d+/m) do |m|
m.to_i * 10
end
puts new_list
# >> 30 apples
# >> 5000g flour
# >> 10 ham
Documentation: String#gsub.

Resources