How to populate a 2D array with random elements? - ruby

For a Memory game, I'm trying to randomly populate a 2D array with letters.
The grid is size × size and each letter occurs twice.
This is my code so far. I don't understand why it is giving an error
alpha = ("A".."Z").to_a
letters_range = alpha[0...size*size/2]
chosen_letters = (letters_range + letters_range).shuffle
(0...size).each do |row|
(0...size).each do |col|
letter = chosen_letters.select
#grid[row][col] = Card.new(letter)
letter_idx = chosen_letters.index(letter)
chosen_letters.delete_at(letter_idx) #error line
end
end

chosen_letters is an array containing single-character strings.
When running letter = chosen_letters.select, you may assume that Array#select returns a random element. However, when not passing it a block, it returns an Enumerator. As such, your letter variable does not contain an element from the chosen_letter array and thus, an index for this object can not be found, resulting in letter_idx to be nil.
To fix this, you may want to use a more appropriate statement to fetch an element, e.g. Array#pop to return and remove the last element from the array.

Since chosen_letters is already shuffled, you don't have to pick a random element. Instead you can just pop the last or shift the first element off the array which makes your loop a lot simpler:
(0...size).each do |row|
(0...size).each do |col|
letter = chosen_letters.shift
#grid[row][col] = Card.new(letter)
end
end
It might be a little cleaner to start with an array of cards, e.g. by creating a pair of two cards for each letter:
cards = letters_range.flat_map { |letter| [Card.new(letter), Card.new(letter)] }
cards.shuffle!
You can then assign the cards to the grid via:
(0...size).each do |row|
(0...size).each do |col|
#grid[row][col] = cards.shift
end
end
or you could build the #grid right out of the array via each_slice:
#grid = cards.each_slice(size).to_a

Related

Ruby calling method in quick_sort gives wrong output

I am doing a quick sort in ruby and using quick_sort I am grabbing the last 3 elements of the array and multiplying it to get the value.
My quick sort works fine but only thing is when I call the my other method max_product_three in quick_search the sorted_array I pass into the method is only showing up as two random numbers like [-3,-2]. If I take out my method from quick_search it gives the correct output. What is happening that when I put my method the sorted array is wrong which is causing my max_product_three not to work.
quick_search.rb
def quick_search(array)
return array if array.length <= 1
len = array.length - 1
left = []
right = []
pivot = array.sample
array.delete_at(array.index(pivot))
array.each do |num|
if num < pivot
left << num
else
right << num
end
end
sorted = []
sorted << quick_search(left)
sorted << pivot
sorted << quick_search(right)
sorted_array = sorted.flatten
p sorted_array
max_product_three(sorted_array)
end
max_product_three.rb
def max_product_three(sorted_array)
len = sorted_array.length - 1
take_3 = len - 3
mulitple = sorted_array.drop(take_3)
p mulitple.inject(:*)
end
I am using this array as reference [-3,1,2,-2,5,6]
I assume you're writing this to learn how quick-sort works? Because ruby's got a #sort method right there... 😁
There are few things here. Is your intention to do two things: first, sort the array; second, multiply the three largest values in the sorted array? Because that's not what your code currently does. You call your max_product_three method from within your sort, which means it'll be called every time quick_sort is called.
Worse, it's the last line in the method. That means the result of calling max_product_three is what's returned each time you iterate, not the sorted array! So, for each sub-sort, what you get back is a single number instead of the sorted array.
Also, your max_product_three method multiplies the last 4 values, not the last 3 values. (You subtract 1 from its length and then subtract 3 from it, so you're dropping length - 4 values, leaving 4 values to multiply.)
You don't need to do p sorted_array at the end of your quick_search method (presumably, should be quick_sort!) but can just have sorted_array to return the array.
And, a smaller thing, your initial guard clause would be a bit better (and more ruby-ish) by not using an explicit operator, for example:
return array unless array.length.positive?
That's quite a lot, and I may have misinterpreted what you're trying to do here so let me know if I have!

Is there a better way?: iterating over an array in ruby

I'm working on a mini project for a summer class. I'd like some feedback on the code I have written, especially part 3.
Here's the question:
Create an array called numbers containing the integers 1 - 10 and assign it to a variable.
Create an empty array called even_numbers.
Create a method that iterates over the array. Place all even numbers in the array even_numbers.
Print the array even_numbers.
Here's my code, so far:
numbers = [1,2,3,4,5,6,7,8,9,10]
print numbers[3]
even_numbers.empty?
def even_numbers
numbers.sort!
end
Rather than doing explicit iteration, the best way is likely Array#select thus:
even_numbers = numbers.select { |n| n.even? }
which will run the block given on each element in the array numbers and produce an array containing all elements for which the block returned true.
or an alternative solution following the convention of your problem:
def get_even_numbers(array)
even_num = []
array.each do |n|
even_num << n if n.even?
end
even_num
end
and of course going for the select method is always preferred.

Summing all numbers in an array

I want to put a possibly infinite amount of numbers in and then it's added to an array, which is then all added together.
I saw this on a few other questions but they were all just puts-ing the array, not summing.
case input
when 'add'
puts "Enter the numbers to add on separate lines then hit enter on another line"
add_array = []
numbers_to_add = " "
while numbers_to_add != ""
numbers_to_add = gets.chomp
add_array.push numbers_to_add
end
add_array.delete('')
add_array.map(&:to_f).inject(:+)
puts add_array
end
You can utilize the inject method.
[1,2,3].inject(:+) #=> 6
By the looks of your code I'd guess that your incoming array is an array of strings, not an array of numbers. To convert them to decimals (floats) you can use:
sum = add_array.map(&:to_f).inject(:+)
puts sum
This applies the #to_f operation on every element, then passes that to the summing function (#inject(:+))

ruby string array iteration. Array of arrays

I have a ruby problem
Here's what i'm trying to do
def iterate1 #define method in given class
#var3 = #var2.split(" ") #split string to array
#var4 = #var3
#var4.each do |i| #for each array item do i
ra = []
i.each_char {|d| ra << counter1(d)} # for each char in i, apply def counter1
#sum = ra.inject(:+)
#sum2 = #sum.inject(:+) #have to do the inject twice to get values
end
#sum2
I know i have over complicated this
Basically the input is a string of letters and values like "14556 this word 398"
I am trying to sum the numbers in each value, seperated by the whitespace like (" ")
When i use the def iterate1 method the block calls the counter1 method just fine, but i can only get the value for the last word or value in the string.
In this case that's 398, which when summed would be 27.
If i include a break i get the first value, which would be 21.
I'm looking to output an array with all of the summed values
Any help would be greatly appreciated
I think you're after:
"10 d 20 c".scan(/\b\d+\b/).map(&:to_i).inject(:+) # Returns 30
scan(/\b\d+\b/) will extract all numbers that are made up of digits only in an array, map(&:to_i) will convert them to integers and I guess you already know what inject(:+) will do.
I'm not sure if I understand what you're after correctly, though, so it might help if you provide the answer you expect to this input.
EDIT:
If you want to sum the digits in each number, you can do it with:
"12 d 34 c".scan(/\b\d+\b/).map { |x| x.chars.map(&:to_i).inject(:+) }
x.chars will return an enumerator for the digits, map(&:to_i) will convert them to integers and inject(:+) will sum them.
The simplest answer is to use map instead of each because the former collects the results and returns an array. e.g:
def iterate1 #define method in given class
#var3 = #var2.split(" ") #split string to array
#var4 = #var3
#var4.map do |i| #for each array item do i
ra = []
i.each_char {|d| ra << counter1(d)} # for each char in i, apply def counter1
#sum = ra.inject(:+)
#sum2 = #sum.inject(:+) #have to do the inject twice to get values
end
end
You could write it a lot cleaner though and I think Stefan was a big help. You could solve the issue with a little modification of his code
# when you call iterate, you should pass in the value
# even if you have an instance variable available (e.g. #var2)
def iterate(thing)
thing.scan(/\b\d+\b/).map do |x|
x.chars.map{|d| counter1(d)}.inject(:+)
end
end
The above assumes that the counter1 method returns back the value as an integer

Storing output into a variable to be used in an array

A snippet of my code below flips a coin and outputs a result of 10 total heads or tails.
(e.g. Heads Tails Heads Tails...)
I'd like to store this into a variable where I can put it into an array and use its strings.
%w[act] only outputs the string "act". How can I get that line of code to output my array of strings from the line act = coin.flip?
Updated and added full code
class Coin
def flip
flip = 1 + rand(2)
if flip == 2
then puts "Heads"
else
puts "Tails"
end
end
end
array = []
10.times do
coin = Coin.new
array << coin.flip
end
puts array
This:
10.times do
coin = Coin.new
act = coin.flip
end
doesn't produce an array. It simply creates ten coin flips and throws them all away, the result of that expression is, in fact, 10. If you want an array, you'll need to build one.
You could take Douglas's approach or try something a bit more idiomatic.
The Integer#times method returns an enumerator so you can use any of the Enumerable methods on it rather than directly handing it a block. In particular, you could use collect to build an array in one nice short piece of code:
a = 10.times.collect { Coin.new.flip }
That gives you 10 flips in the Array a and then you can puts a or puts a.join(', ') or whatever you want.
The %w[] won't work because that's for generating an Array of whitespace separated words:
%w[] Non-interpolated Array of words, separated by whitespace
So %w[a b c] is just a nicer way of saying ['a', 'b', 'c'] and the words within %w[] are treated as single quoted strings rather than variables or method calls to be evaluated.
Seems that there is some editing going on. You'll also want to modify your flip method to return the flip rather than print it:
def flip
flip = 1 + rand(2)
if flip == 2
"Heads"
else
"Tails"
end
end
Then you'll get your Heads and Rails in the array.
Put the act results into an array.
arr = []
10.times do
coin = Coin.new
arr << coin.flip
end
p arr # => [...]

Resources