Alphabetizing code leaves a superfluous `""` in my sorted array - arrays

Why does the following code leave an extra "" in my final array?
puts 'Type in as many words as you\'d like. When you\'re finished, press enter on an empty line'
array = []
input = ' '
while input != ''
input = gets.chomp
array.push input
end
array.sort

You push input onto the array regardless of whether there was any or not. Since you want to check the terminating condition between reading and pushing, this is a good place to use a break:
puts 'Type in as many words as you\'d like. When you\'re finished, press enter on an empty line'
array = []
loop do
input = gets.chomp
break if input.empty?
array.push input
end
puts
puts array.sort

As Stefan mentioned in the comment it is because you're always pushing what you get.
You should swap the two methods inside the loop so that it checks the condition between the gets and the push.
puts 'Type in as many words as you\'d like. When you\'re finished, press enter on an empty line'
array = []
input = gets.chomp
until input.empty?
array.push input
input = gets.chomp
end

Related

Ruby partner pairing program not working?

I am new to ruby and have this program that takes in a number of names and sorting them into pairs of two, and throwing the odd person in a random group. Sometimes it works perfect, sometimes it throws the extra person into an array of their own, and im not sure why. I know there is a cleaner way to do this but Im just trying to understand how the code works. For example it should return "Apple" "Banana" "Orange" as ["Banana", "Orange", "Apple"] and will most of the time, but sometimes it give me ["Banana","Orange",] ["Apple"] Any advice?
def randomArray
classNames = []
puts "Please enter a list of names to sort"
while true
input = gets.chomp
break if input.empty?
classNames << input
end
classRandom = classNames.shuffle
splitNames = classRandom.each_slice(2).to_a
arrayPos = 0
splitNames.length.times do
if splitNames[arrayPos].length == 2
arrayPos+=1
else splitNames[arrayPos].length == 1
splitNames.sample << splitNames[arrayPos].pop
arrayPos+=1
end
end
x = 0
splitNames.length.times do
break if splitNames[x].empty?
puts "Group number #{x+1} is #{splitNames[x]}"
x+=1
end
end
randomArray
Your problem is this: splitNames.sample << splitNames[arrayPos].pop
sample can return any element of the array, including the element that has the odd person you're trying to assign! So if it samples that person, it removes them from their group of 1 and then adds them right back in.
To fix it, take advantage of the fact that either all groups will be pairs, or the last group will have a single person. Don't iterate over the array, just check splitNames[-1]. If they are alone, add them to splitNames[0...-1].sample.

ruby compare string with file contents

My end goal is to compare values from a file to the records I’m pulling back from a database.
I have a text file with the following structure:
123,55,66
555,99,109
324,100,800
467,200,300
I then pull back records from a database and loop through them one by one.
sth.fetch do |row|
validate = "#{row[0]},#{row[20]},#{row[21]}"
names = File.readlines('results.txt')
matches = names.select { |name| name[/#{validate}/i] }
p "Result matches as expected: #{matches}"
end
so validate builds me the same format string as per the examples above... I then read all lines of the file and move them into an array and then compare. So this is great it gives me all matches but doesn't tell me rows that don't exist on the file?
So alternatively I tried
sth.fetch do |row|
File.open('results') do |f|
f.each_line do |line|
if line == validate
puts "detail: #{line}"
else
puts "detail: #{validate}"
end
end
end
end
But this then reads each line for every row and compares so it half achieves what I am after.
So I want to take my 'validate' string and read the contents of the file to see if a row matches and then print out it's a match. Alternatively if I pull back a record and it doesn't match the file then I just want one does not match message.
Thanks
I would try the following
names = File.readlines('results.txt').map(&:chomp)
sth.fetch do |row|
validate = "#{row[0]},#{row[20]},#{row[21]}"
# for direct compare
# if names.include?(validate)
# Or for Regexp
if names.grep(/#{validate}/i).any?
p "Result matches as expected: #{matches}"
else
p "Result does not matches as expected: #{matches}"
end
end
all_matches.uniq!

Checking if an element belongs to an array in Ruby

I'm trying to figure out how to check if an element belongs to an array using ruby. I'm reading from a file (say demo.txt) which has comma separated pairs in each line. I'm concatenating these two pairs and pushing it into an array. Later I need to check if a specific value belongs to the array. I can see the array is populated successfully but the second check is unsuccessful, i.e. it can't find the element in the array. 'My demo.txt' is as follows
a, b
c, d
The ruby code goes as follows
array = Array.new
File.readlines('demo.txt').each do |line|
line.slice! ", "
array.push line
end
array.each do|d|
puts d
end
if array.include? 'ab'
puts "correct" #this is not printed
end
How do I check if the array contains the element 'ab'?
There is ab\n in your array.
Instead of
array.each do|d|
puts d
end
use inspect to check the values,
p array
#=> ["ab\n", "cd"]
To fix the issue, use chomp on line
File.readlines('b.txt').each do |line|
line = line.chomp
...
end
Replacing this
if array.include? 'ab'
puts "correct"
end
with this one
if array.find 'ab'
puts "correct"
end
resolves the issue.

Strange result when using #each_with_index to change an array

I'm trying to write some code that will loop through an array of strings, clean up the entries, and then add the cleaned up entries to a hash that tracks the frequency with which each word appears. This was my first solution:
puts("Give me your text.")
text = gets.chomp
words = text.split
frequencies = Hash.new(0)
words.map! do |word|
word.tr("\",.", "")
end
words.each do |word|
frequencies[word] += 1
end
It works fine, but looping through the array twice feels very inefficient, so I've been trying to find a way to do it one go and stumbled upon the following:
puts("Give me your text.")
text = gets.chomp
words = text.split
frequencies = Hash.new(0)
words.each_with_index do |word, index|
words[index].tr!("\",.", "")
frequencies[word] += 1
end
Based on my understanding of each_with_index, this shouldn't work, but somehow it does, and the hash receives the clean version of each string: https://repl.it/B9Gw. What's going on here? And is there a different way to solve this problem without looping twice?
EDIT: After some reading, I was able to solve the problem using just one loop in the following way:
puts("Give me your text.")
text = gets.chomp
words = text.split
frequencies = Hash.new(0)
for i in 0..words.length-1
words[i].tr!("\",.", "")
frequencies[words[i]] += 1
end
However, this is more of a JS or C++ solution and doesn't seem like idiomatic Ruby. Are there any other options? Also, why does the each_with_index approach even work?
You are using the String#tr! method, which modifies the string destructively instead of returning a new string. The fact that you are looking it up on the hash again (using words[index]) doesn't change anything, because the string object is still the same - so the word you use to modify the frequencies hash is also modified.
And is there a different way to solve this problem without looping twice?
An obvious way would be to use the same logic that you used, but without the with_index (which isn't making any difference here anyway). I would advise using the non-destructive String#tr instead of String#tr!, to make it more clear which strings have been cleaned and which have not.
frequencies = Hash.new(0)
words.each do |word|
cleaned = word.tr("\",.", "")
frequencies[cleaned] += 1
end
If you want to make clear the map phase of the process and still only loop once, you can leverage ruby's lazy enumerators:
frequencies = Hash.new(0)
cleaned_words = words.lazy.map { |word| word.tr("\",.", "") }
cleaned_words.each do |cleaned|
frequencies[cleaned] += 1
end
Here, even though we do a map and then an each, the collection is only traversed once, and ruby doesn't create any intermediary arrays.

Ruby Array/Loop/.each do - Simplest Answer

In Ruby I'm looking to create a short program where a user is able to enter a certain number of words (5 for now) and the program needs to spit the words back out to the user in alphabetical order, and each word must alternate from ALL CAPS to all lowercase letters.
This program can only use methods like (.each do, .sort, .upcase, .downcase), arrays, loops, and if/else statements. NOTHING ELSE ... so no indexes, dictionaries, modules, arguments, etc. The syntax shouldn't diverge too much from what I've already written.
This is the code I have so far ... I keep getting a no method error ... any help would be greatly appreciated.
words = []
5.times do
puts "Please enter a word"
words << gets.chomp
end
words.sort.each do |odd, even|
if odd puts words.upcase
elsif even puts words.downcase
end
end
The first loop, filling the array, is fine.
words.sort.each do |odd, even|
if odd puts words.upcase
elsif even puts words.downcase
end
end
This does not work. each takes one word from the sorted array and "feeds" it to the block. The block uses the variable "odd" for it. Since the variable "even" did not get a value, it will be nil.
In the next line Ruby decides if odd is true, and since it is not falseor nil, Ruby decides it is, so puts words.upcase is executed. words however, being an Array, does not know about upcasing things - and tells you so by giving an error.
The following code uses a variable as a "toggle"; it is switched on if it's off and vice versa.
words = []
5.times do
puts "Please enter a word"
words << gets.chomp
end
up = true # the toggle
words.sort.each do |word|
if up then
puts word.upcase
up = false
else
puts word.downcase
up = true
end
end
Edit: I had not seen the post by #steenslag when I posted this answer, but it is slightly different in the way the input is generated so I will leave it unless it is desired that I remove it, in which case I will.
puts "Enter 5 words separated by spaces: "
words = gets.chomp!
results = words.split(" ").sort!
caps = true
results.each do |word|
if caps == true
puts word.upcase
caps = false
elsif caps == false
puts word.downcase
caps = true
end
end
I agree that the idea with the toggle is the way to go as other anwers already suggest.
I would just simplify the block a bit, because in Ruby is no need to check the toggle explicitly against true or false nor you need change the toggle explicitly to true or false in each iteration, just toggle it:
upcase = true
words.sort.each do |word|
puts(upcase ? word.upcase : word.downcase)
upcase = !upcase
end

Resources