ruby 1.9 how to convert array to string without brackets - ruby

My question is about how to convert array elements to string in ruby 1.9 without getting the brackets and quotation marks. I've got an array (DB extract), from which I want to use to create a periodic report.
myArray = ["Apple", "Pear", "Banana", "2", "15", "12"]
In ruby 1.8 I had the following line
reportStr = "In the first quarter we sold " + myArray[3].to_s + " " + myArray[0].to_s + "(s)."
puts reportStr
Which produced the (wanted) output
In the first quarter we sold 2 Apple(s).
The same two lines in ruby 1.9 produce (not wanted)
In the first quarter we sold ["2"] ["Apple"] (s).
After reading in the documentation
Ruby 1.9.3 doc#Array#slice
I thought I could produce code like
reportStr = "In the first quarter we sold " + myArray[3] + " " + myArray[0] + "(s)."
puts reportStr
which returns a runtime error
/home/test/example.rb:450:in `+': can't convert Array into String (TypeError)
My current solution is to remove brackets and quotation marks with a temporary string, like
tempStr0 = myArray[0].to_s
myLength = tempStr0.length
tempStr0 = tempStr0[2..myLength-3]
tempStr3 = myArray[3].to_s
myLength = tempStr3.length
tempStr3 = tempStr3[2..myLength-3]
reportStr = "In the first quarter we sold " + tempStr3 + " " + tempStr0 + "(s)."
puts reportStr
which in general works.
However, what would be a more elegant "ruby" way how to do that?

You can use the .join method.
For example:
my_array = ["Apple", "Pear", "Banana"]
my_array.join(', ') # returns string separating array elements with arg to `join`
=> Apple, Pear, Banana

Use interpolation instead of concatenation:
reportStr = "In the first quarter we sold #{myArray[3]} #{myArray[0]}(s)."
It's more idiomatic, more efficient, requires less typing and automatically calls to_s for you.

And if you need to do this for more than one fruit the best way is to transform the array and the use the each statement.
myArray = ["Apple", "Pear", "Banana", "2", "1", "12"]
num_of_products = 3
tranformed = myArray.each_slice(num_of_products).to_a.transpose
p tranformed #=> [["Apple", "2"], ["Pear", "1"], ["Banana", "12"]]
tranformed.each do |fruit, amount|
puts "In the first quarter we sold #{amount} #{fruit}#{amount=='1' ? '':'s'}."
end
#=>
#In the first quarter we sold 2 Apples.
#In the first quarter we sold 1 Pear.
#In the first quarter we sold 12 Bananas.

You can think of this as arrayToString()
array = array * " "
E.g.,
myArray = ["One.","_1_?! Really?!","Yes!"]
=> "One.","_1_?! Really?!","Yes!"
myArray = myArray * " "
=> "One. _1_?! Really?! Yes."

Related

How to loop nested arrays in Ruby

VERY new to Ruby and coding in general. I'm trying to loop through two dimensional arrays but can't figure it out. Here's what I have:
--Use a loop to print out each person on separate lines with their alter egos.
--Bruce Wayne, a.k.a. Batman
people = [
["Bruce", "Wayne", "Batman"],
["Selina", "Kyle", "Catwoman"],
["Barbara", "Gordon", "Oracle"],
["Terry", "McGinnis", "Batman Beyond"]
]
index = people[0][0]
first_name = people[0][0]
last_name = people[0][1]
hero_name = people[0][2]
4.times do
puts first_name + " " + last_name + "," " " + "a.k.a" " " + hero_name
index = index + 1
end
It does print the first line but then raises an error:
Bruce Wayne, a.k.a Batman
# `+': no implicit conversion of Integer into String (TypeError)
In ruby we don’t use loops by index, like for and family; instead we iterate on collections:
people =
[["Bruce", "Wayne", "Batman"],
["Selina", "Kyle", "Catwoman"],
["Barbara", "Gordon", "Oracle"],
["Terry", "McGinnis", "Batman Beyond"]]
people.each do |first, last, nick|
puts "#{first} #{last}, a.k.a #{nick}"
end
or
people.each do |first_last_nick|
*first_last, nick = first_last_nick
puts [first_last.join(' '), nick].join(', a.k.a ')
end
Your code produces error because you assign a String to index
index = people[0][0]
and then you use it to count with
index = index + 1
You could have used
index = 0
and
index += 1
A more Rubyesque way would be to enumerate the array and print it like this
people.each do |person|
puts "#{person.first} #{person[1]}, a.k.a #{person.last}"
end
Which gives
Bruce Wayne, a.k.a Batman
Selina Kyle, a.k.a Catwoman
Barbara Gordon, a.k.a Oracle
Terry McGinnis, a.k.a Batman Beyond
Storing the parts in a variable improves readability but lenghtens the code which in turn diminishes readability, the choice is yours..
As an alternative you could name the indices or decompose like mudasobwa suggests.
Firstname, Lastname, Nickname = 0, 1, 2
people.each do |person|
puts "#{person[Firstname]} #{person[Lastname]}, a.k.a #{person[Nickname]}"
end
For your code to work:
4.times do |character|
puts people[character][0] + " " + people[character][1] + "," " " + "a.k.a" " " + people[character][2]
end
But iterating in ruby is done as answered by others.
This is a version using a block {} instead:
people = [["Bruce", "Wayne", "Batman"], ["Selina", "Kyle", "Catwoman"], ["Barbara", "Gordon", "Oracle"], ["Terry", "McGinnis", "Batman Beyond"]]
people.each { |character| puts "#{character [0]}, a.k.a #{character [1]} #{character [2]}" }
#=> Bruce, a.k.a Wayne Batman
#=> Selina, a.k.a Kyle Catwoman
#=> Barbara, a.k.a Gordon Oracle
#=> Terry, a.k.a McGinnis Batman Beyond
In general to loop through nested arrays:
people.each do |character|
character.each do |name|
puts name
end
end

.split() method not working on ruby

I'm trying to make a simple algebra calculator app on ruby, but I encountered a problem while coding it. The .split method, which I was using to divide the equation into "sections" (separated by plus and minus signs), did split the equation, eq, with + signs, but it didn't with - signs.
eq = gets.chomp
a = []
a = eq.split("+")
a.each do |n|
case n
when n.include?("-")
a << n.split("-")
end
end
print a[0], ";", a[1]
I used the case when because if I did not, it returned a NoMethod Error. I already made a regular calculator, so I thought this would made a good next-project. I was also wondering if you had any idea to make my code shorter; maybe by creating a method. Below is my regular calculator's code, which I would also like to know how to make shorter.
loop do
print
equation = gets.chomp
if equation.include?"^"
exponent_e = equation.split("^")
result_e = exponent_e[0].to_f ** exponent_e[1].to_f
print " = #{result_e} "
puts
elsif equation.include?"%"
percent_e = equation.split("%")
number = percent_e[0].to_f / 100
result_p = number * percent_e[1].to_f
print " = #{result_p} "
puts
elsif equation.include?"/"
res_d = equation.split("/")
result_d = res_d[0].to_f / res_d[1].to_f
print " = #{result_d} "
puts
elsif equation.include?"*"
res_m = equation.split("*")
result_m = res_m[0].to_f * res_m[1].to_f
print " = #{result_m} "
puts
elsif equation.include?"+"
res_a = equation.split("+")
result_a = res_a[0].to_f + res_a[1].to_f
print " = #{result_a} "
puts
elsif equation.include?"-"
res_s = equation.split("-")
result_s = res_s[0].to_f - res_s[1].to_f
print " = #{result_s} "
puts
else
puts "Input valid equation"
end
end
The argument passed to the split method will split up your string by the argument passed and return an array with everything else all split up.
For example:
"a+b".split("+")
#=> ["a", "b"]
"c-d".split("+")
#=> ["c-d"]
"c-d".split("-")
#=> ["c", "d"]
I would probably refactor the code by either using OOP creating a class Calculator and then creating the methods for each functionality (i.e. plus, minus, divide, etc..). This would make the code more readable and easier to maintain.
Another cool thing to consider is using metaprogramming.
def calculate(fxn, arr_numbers)
if arr_numbers.size == 2
arr_numbers.send(:reduce, fxn)
end
end
Where fxn is a string (i.e. "+", "-", etc..), and arr_numbers is an array of 2 numbers, not strings (i.e. [2, 5])
You can expand this to take multiple numbers as well or add other functionality..

How do I add these two variables?

puts "first number please"
first = gets.chomp
puts "Second number please"
second = gets.chomp
answer = first + second
puts "The calculation is #{first} + #{second} = " + answer.to_s
I summed two variables first and second
If first == 1 and second == 2 then answer should be 3, but ruby shows 12 What is the problem?
What I tried is
answer = first.+(second)
Thant's because gets returns a string. So the + operator in answer = first + second applies to string concatenation. Change it to:
puts "first number please"
first = gets.to_i
puts "Second number please"
second = gets.to_i
So your problem here is that you are trying to get sum of 2 strings which is not going to work, you need to first turn it into a integer by replacing .chomp with .to_i and then you can use it like you were using it, but remember that if you want decimal number for example if you are doing division you need to use .to_f to make it float to get the more accurate answer in decimals
puts("Give first number")
number_one = gets.to_i
puts("Give second number")
number_two = gets.to_i
sum = number_one + number_two
puts("Answer is: #{sum}")
The numbers you got were actually strings, so when you used "+" ruby concatenated them.
You should try
gets.to_i
puts "first number please"
first = gets.chomp
puts "Second number please"
second = gets.chomp
answer = first.to_i + second.to_i
puts "The calculation is #{first} + #{second} = #{answer}"
Console:
[3] a = gets.chomp
2
=> "2"
[4] a.class
=> String
[5] a = a.to_i
=> 2
[6] a.class
=> Fixnum

ruby searching array for keywords

I am parsing a large CSV file in a ruby script and need to find the closest match for a title from some search keys. The search keys maybe one or more values and the values may not exactly match as per below (should be close)
search_keys = ["big", "bear"]
A large array containing data that I need to search through, only want to search on the title column:
array = [
["id", "title", "code", "description"],
["1", "once upon a time", "3241", "a classic story"],
["2", "a big bad wolf", "4235", "a little scary"],
["3", "three big bears", "2626", "a heart warmer"]
]
In this case I would want it to return the row ["3", "three big bears", "2626", "a heart warmer"] as this is the closest match to my search keys.
I want it to return the closest match from the search keys given.
Is there any helpers/libraries/gems I can use? Anyone done this before??
I am worried, this task should be handled to any search engine at db level or similar, no point fetching data in app and do searching across columns/rows etc, should be expensive. but for now here is the plain simple approach :)
array = [
["id", "title", "code", "description"],
["1", "once upon a time", "3241", "a classic story"],
["2", "a big bad wolf", "4235", "a little scary"],
["3", "three big bears", "2626", "a heart warmer"]
]
h = {}
search_keys = ["big", "bear"]
array[1..-1].each do |rec|
rec_id = rec[0].to_i
search_keys.each do |key|
if rec[1].include? key
h[rec_id] = h[rec_id] ? (h[rec_id]+1) : 1
end
end
end
closest = h.keys.first
h.each do |rec, count|
closest = rec if h[closest] < h[rec]
end
array[closest] # => desired output :)
I think you can do it by your self and no need to use any gems!
This may be close to what you need; searching in the array for the keys and set a rank for each found element.
result = []
array.each do |ar|
rank = 0
search_keys.each do |key|
if ar[1].include?(key)
rank += 1
end
end
if rank > 0
result << [rank, ar]
end
end
This code can be written better than the above, but i wanted to show you the details.
This works. Will find and return an array of matched* rows as result.
*matched rows = a row where the id, title, code or description match ANY of the provided seach_keys. incl partial searches such as 'bear' in 'bears'
result = []
array.each do |a|
a.each do |i|
search_keys.each do |k|
result << a if i.include?(k)
end
end
end
result.uniq!
You could probably write it in a more succinct way...
array = [
["id", "title", "code", "description"],
["1", "once upon a time", "3241", "a classic story"],
["2", "a big bad wolf", "4235", "a little scary"],
["3", "three big bears", "2626", "a heart warmer"]
]
search_keys = ["big", "bear"]
def sift(records, target_field, search_keys)
# find target_field index
target_field_index = nil
records.first.each_with_index do |e, i|
if e == target_field
target_field_index = i
break
end
end
if target_field_index.nil?
raise "Target field was not found"
end
# sums up which records have a match and how many keys they match
# key => val = record => number of keys matched
counter = Hash.new(0) # each new hash key is init'd with value of 0
records.each do |record| # look at all our given records
search_keys.each do |key| # check each search key on the field
if record[target_field_index].include?(key)
counter[record] += 1 # found a key, init to 0 if required and increment count
end
end
end
# find the result with the most search key matches
top_result = counter.to_a.reduce do |top, record|
if record[1] > top[1] # [0] = record, [1] = key hit count
top = record # set to new top
end
top # continue with reduce
end.first # only care about the record (not the key hit count)
end
puts "Top result: #{sift array, 'title', search_keys}"
# => Top result: ["3", "three big bears", "2626", "a heart warmer"]
Here is my one-line shot
p array.find_all {|a|a.join.scan(/#{search_keys.join("|")}/).length==search_keys.length}
=>[["3", "three big bears", "2626", "a heart warmer"]]
to get all the rows in order of number of matches
p array.drop(1).sort_by {|a|a.join.scan(/#{search_keys.join("|")}/).length}.reverse
Anyone knows how to combine the last solution so that the rows that contain none of the keys are dropped and to keep it concise as is ?

How do I use the for loop in ruby to grab different values from hash tables

This is probably easy to do! I'm not able envision the loop yet, I was thinking about a nested for loop but not quite sure how to alternate between the two hashes.
Lets say I have a class with a def that containts two hash tables:
class Teststuff
def test_stuff
letters = { "0" => " A ", "1" => " B ", "2" => " C " }
position = {"1" => "one ", "2"=> " two ", "3"=> " three ", "4"=>" four " }
my_array=[0,1,2,2] #this represents user input stored in an array valid to 4 elements
array_size = my_array.size #this represents the size of the user inputed array
element_indexer = my_array.size # parellel assignment so I can use same array for array in dex
array_start_index = element_indexer-1 #give me the ability later to get start at index zero for my array
#for loop?? downto upto??
# trying to get loop to grab the first number "0" in element position "0", grab the hash values then
# the loop repeats and grabs the second number "1" in element position "1" grab the hash values
# the loop repeats and grabs the third number "2" in elements position "2" grab the hash values
# the final iteration grabs the fourth number "2" in elements position "3" grab the hash values
# all this gets returned when called. Out put from puts statement after grabing hash values
# is: **A one B two C three C four**
return a_string
end
end
How do I go about returning string output to the screen like this:
**A one B two C three C four**
or simply letter position letter position...
Thanks for the help, put code up so I can try on my editor!
I think I figured out what it is you want, although I still have no idea what array_size, element_indexer, array_start_index and TestStuff are for.
def test_stuff
letters = { "0" => " A ", "1" => " B ", "2" => " C " }
position = {"1" => "one ", "2"=> " two ", "3"=> " three ", "4"=>" four " }
my_array = [0, 1, 2, 2]
"**#{my_array.map.with_index {|e, i|
"#{letters[e.to_s].strip} #{position[(i+1).to_s].strip}"
}.join(' ')}**"
end
[I took the liberty of reformatting your code to standard Ruby coding style.]
However, everything would be much simpler, if there weren't all those type conversions, and all those superfluous spaces. Also, the method would be much more useful, if it actually had a way to return different results, instead of always returning the same thing, because at the moment, it is actually exactly equivalent to
def test_stuff
'**A one B two C three C four**'
end
Something along these lines would make much more sense:
def test_stuff(*args)
letters = %w[A B C]
position = %w[one two three four]
"**#{args.map.with_index {|e, i| "#{letters[e]} #{position[i]}" }.join(' ')}**"
end
test_stuff(0, 1, 2, 2)
# => '**A one B two C three C four**'
If you don't want to pollute the Object namespace with your method, you could do something like this:
def (TestStuff = Object.new).test_stuff(*args)
letters = %w[A B C]
position = %w[one two three four]
"**#{args.map.with_index {|e, i| "#{letters[e]} #{position[i]}" }.join(' ')}**"
end
TestStuff.test_stuff(0, 1, 2, 2)
# => '**A one B two C three C four**'
You can use enumerators, like this:
l = letters.to_enum
p = position.to_enum
a_string = ''
loop do
a_string << l.next[1] << p.next[1]
end
How about :
a_string = ""
my_array.each_with_index { |x, index|
a_string += letters[my_array[index].to_s] + " " + (position.include?((index+1).to_s) ? position[(index+1).to_s] : "nil")
}

Resources