Ruby Array/Loop/.each do - Simplest Answer - arrays

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

Related

How to continue iteration over a codition even after it has been met once or even multiple times in ruby

I am writing a small package manager in ruby, and while working on its' package searching functionality, I want to continue iterating over a list of matches, even if it has found a package or sting identical to the inputted string.
def makelist(jsn, searchterm)
len = jsn.length
n = 0
while n < len do
pkname = jsn[n]["Name"]
pkdesc = jsn[n]["Description"]
pkver = jsn[n]["Version"]
unless pkname != nil || pkdesc != nil
# skip
else
puts "#{fmt(fmt("aur/", 6),0)}#{fmt(pkname,0)} [#{fmt(pkver,8)}]\n #{pkdesc}"
n += 1
end
end
end
I have tried using an if statement, unless statement and a case statement in which I gave conditions for what it should do if it specifically finds a packages that matches searchterm but when I use this condition, it always skips all other conditions and ends the loop, printing only that result. This block of code works, but I want to use my fmt function to format the text of matches differently in the list. Anyone have any ideas of how I could accomplish this?
EDIT:
Based on some back and forth the desired behavior is to print matched results differently from unmatched results.
So, in Ruby you have access to "functional" patterns like select, map, reduce, etc. that can be useful for what you're doing here.
It's also advantageous to separate functionality into different methods (e.g. one that searches, one that turns them into a string, one that prints the output). This is just an example of how to break it down, but splitting up the responsibilities makes it much easier to test and see which function isn't doing what you want by examining the intermediate structures.
Also, I'm not sure how you want to "match" the search term, so I used String#include? but you can replace that with whatever matching algorithm you like.
But I think you're looking for something like this:
def makelist(jsn, searchterm)
jsn.select do |package|
package["Name"] && package["Description"]
end.map do |package|
if matches?(package, searchterm)
matched_package_to_string(package)
else
package_to_string(package)
end
end.join("\n")
end
def matches?(package, searchterm)
package['Name'].include?(searchterm) || package('Description').include?(searchterm)
end
def matched_package_to_string(package)
pkname = package["Name"]
pkdesc = package["Description"]
pkver = package["Version"]
"#{fmt(fmt("aur/", 6),0)}#{fmt(pkname,0)} [#{fmt(pkver,8)}]\n #{pkdesc}"
end
def package_to_string(package)
# Not sure how these should print
end

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.

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

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

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.

Resources