Ruby each_with_index iteration. Unsure about why else statement is needed - ruby

Why do we need
hash[number] = index
in the following code?
nums = [11, 8, 1, 7]
target = 9
def two_sum(nums, target)
hash = {}
nums.each_with_index do |number, index|
if complement = hash[target - number]
return [complement, index]
end
hash[number] = index
end
end
iteration does:
nums[0] is 11:
target - number = 2
hash[2] doesn't exist --> we should be able to just forget about this number as number 11 at index 0 can not be part of the solution
nums[1] is 8:
target - number = 1
hash[1] DOES exist and we use it's index (hash[1] = 2) as well as the current index (hash[8] = 1). --> this is our solution and will be returned at
return [complement, index]
the answer I keep getting on why
hash[number] = index
is needed is something on the lines of: "The line hash[number] = index assigns the index of the current number to the hash keyed by the current number. This is important because it allows the function to match the current number with a later number that, together, will add up to the target."
but since we get our result in
return [complement, index]
it seems unnecessary to me to add this line?

Answering my own question after reading through the answer comments (thank you for these!!)
Ask was: output the index of nums of the 2 numbers that add up to the target number.
hash is initially blank and needs to be populated via the else statement in .each
at num[0]: target - num[0] = 9-11 = -2. hash[-2] --> false, hence we move to else statement and populate hash with: hash[11] = 0
at num[1]: target - num[1] = 9-8 = 1. hash[1] --> false, hence we move to else statement and populate hash with: hash[8] = 1
at num[2]: target - num[2] = 9-1 = 8. hash[8] --> true, hence we return index of hash[8] and current index of num.each iteration = [1, 2]
at num[3]: target - num[3] = 9-7 = 2. hash[2] --> false, hence we move to else statement and populate hash with: hash[2] = 3

Related

Syncing a random variable in ruby

I am making a simple Tic tac toe game where the rows are represented by three arrays and columns are items in those arrays. For my computer choice I use two separate functions to get the row and column.
$first = ["-","-","-"]
$secound = ["-","-","-"]
$third = ["-","-","-"]
def get_row
row = nil
case rand(3)
when 0
row = $first
when 1
row = $secound
else
row = $third
end
row
end
def get_col
col = nil
case rand(3)
when 0
col = 0
when 1
col = 1
else
col = 2
end
col
end
I then use a third function that keeps generating a new "guess" until it finds an empty spot, at which point it marks an 0.
def computer_guess
temp = false
try = get_row[get_col]
while temp == false
if try == "-"
get_row[get_col] = "O"
temp = true
else
try = get_row[get_col]
end
end
end
My problem is that i cant return the guess to the same position that i check for validity. Is the there a way to sync these or do i need a different approach?
The problem with your approach is that get_row[get_col] returns a random element every time it is called, so likewise get_row[get_col] = "O" will set a random element to "O". You inspect one element and then set another.
You could fix this quickly by modifying the retrieved element in-place:
if try == "-"
try.replace("O")
# ...
But semantically, I don't like that fix very much. Thinking of a tic-tac-toe board, I'd rather assign an "O" to the free spot than transforming the existing placeholder from "-" into "O".
Did you notice that your get_row method returns an array whereas get_col returns an index? I think this mixture of arrays and indices makes your code a bit convoluted.
It's much easier (in my opinion) to access both, row and column via an index.
To do so, you could put your three rows into another array:
$board = [
['-', '-', '-'],
['-', '-', '-'],
['-', '-', '-']
]
The first row can be accessed via $board[0] and its first element via $board[0][0]. To set an element you'd use: $board[1][2] = 'O' (this sets the middle row's right-most element to "O"). Of course, you can also use variables, e.g. $board[row][col].
With this two-dimensional array, your computer_guess could be rewritten using just two random indices: (get_row and get_col aren't needed anymore)
def computer_guess
loop do
row = rand(3) # random row index
col = rand(3) # random column index
if $board[row][col] == '-' # if corresponding spot is "-"
$board[row][col] = 'O' # set that spot to "O"
break # and break out of the loop
end
end
end
Note however that "blindly" guessing spots until you find a free one might not be the best approach.
Instead, you could generate a list of "free" spots. This can be done by first generating an array of all coordinates1 and then select-ing those row/col pairs whose spot is "-":
def free_spots
coordinates = [0, 1, 2].product([0, 1, 2])
coordinates.select { |row, col| $board[row][col] == '-' }
end
Now you just have to chose a random pair (via sample) and set the corresponding spot to "O":
def computer_guess
row, col = free_spots.sample
$board[row][col] = 'O'
end
1 Array#product returns the Cartesian product of the given arrays. It's an easy way to get all pairs:
[0, 1, 2].product([0, 1, 2])
#=> [[0, 0], [0, 1], [0, 2],
# [1, 0], [1, 1], [1, 2],
# [2, 0], [2, 1], [2, 2]]
Here is another approach. This code imitates game computer against itself. You can easily replace one computer's move with user input.
ROWS = 3
COLUMNS = 3
MAX_TURNS = ROWS * COLUMNS
def random_cell
{ row: rand(ROWS), column: rand(COLUMNS) }
end
def empty_random_cell(board)
while true
cell = random_cell
return cell if board_value(board, cell).nil?
end
end
def board_value(board, cell)
board[cell[:row]][cell[:column]]
end
def turn(board, cell, value)
row = cell[:row]
column = cell[:column]
board[row][column] = value
end
def print_board(board)
ROWS.times do |row|
COLUMNS.times do |column|
value = board[row][column]
print value.nil? ? '_' : value
print ' '
end
puts
end
end
board = Array.new(ROWS) { Array.new(COLUMNS) }
print_board(board)
turns_counter = 0
while true
cell = empty_random_cell(board)
turn(board, cell, 'X')
turns_counter += 1
break if turns_counter == MAX_TURNS
cell = empty_random_cell(board)
turn(board, cell, '0')
turns_counter += 1
break if turns_counter == MAX_TURNS
end
print_board(board)
when there is only one available spot, it might take time to guess it.
So it might be better to collate the available spots and get a random value from there:
def computer_guess
free_spots=[]
free_spots.concat($first.each_with_index.map{|x,i|x=='-' ? ['$first',i] : nil}.compact)
free_spots.concat($secound.each_with_index.map{|x,i|x=='-' ? ['$secound',i] : nil}.compact)
free_spots.concat($third.each_with_index.map{|x,i|x=='-' ? ['$third',i] : nil}.compact)
try=free_spots.sample
eval"#{try[0]}[#{try[1]}]='O'" unless try.nil?
end

Understanding two sums leetcode problem (Ruby)

I am working on the Leetcode Two Sums problem: "Given an array of integers, return indices of the two numbers such that they add up to a specific target.". This is what I have:
def two_sum(nums, target)
hash = {}
nums.each_with_index do |num, index|
diff = target - num
if hash[diff]
return [hash[diff],index]
else
hash[num] = index
end
end
end
The code works, however, I'm not too sure why this works.
So I understand that in the each statement it goes through the numbers and it finds the difference. For example,
nums = [4,2,5,1]
target = 6
On the first loop, the difference is 6-2 = 4. But the hash is obviously empty so it will register num as a key, with the current index as the value. Thus the hash is,
hash = {
4: 0
}
On the second loop, the difference is 6-4 = 2. hash[4] is nil so it will add the current num and index to the dictionary.
hash = {
4: 0
2: 1
}
Like so, wouldn't this keep adding the nums to the hash, because at least in this case there aren't matching key value pairs?
Maybe I am overcomplicating things. If someone could eli5, I would greatly appreciate it. Thank you!
The trick is that we add the value to the hash using the number as the key:
hash[num] = index
but extract it using the diff as the key:
if hash[diff]
So if you have as input:
nums = [4,2,5,1]
target = 6
Then on first step, the difference is 6 - 4 = 2, there's no key 2 (diff) in the map, and we add the key 4 (number) to the map.
On the second step, the difference is 6 - 2 = 4, and there's already a key 4 (diff) in the map, so we return the value.

Speeding up solution to algorithm

Working on the following algorithm:
Given an array of non-negative integers, you are initially positioned
at the first index of the array.
Each element in the array represents your maximum jump length at that
position.
Determine if you are able to reach the last index.
For example:
A = [2,3,1,1,4], return true.
A = [3,2,1,0,4], return false.
Below is my solution. It tries every single potential step, and then memoizes accordingly. So if the first element is three, the code takes three steps, two steps, and one step, and launches three separate functions from there. I then memoized with a hash. My issue is that the code works perfectly fine, but it's timing out for very large inputs. Memoizing helped, but only a little bit. Am I memoizing correctly or is backtracking the wrong approach here?
def can_jump(nums)
#memo = {}
avail?(nums, 0)
end
def avail?(nums, index)
return true if nums.nil? || nums.empty? || nums.length == 1 || index >= nums.length - 1
current = nums[index]
true_count = 0
until current == 0 #try every jump from the val to 1
#memo[index + current] ||= avail?(nums, index + current)
true_count +=1 if #memo[index + current] == true
current -= 1
end
true_count > 0
end
Here's a 𝑂(𝑛) algorithm:
Initialize 𝑚𝑎𝑥 to 0.
For each number 𝑛𝑖 in 𝑁:
If 𝑖 is greater than 𝑚𝑎𝑥, neither 𝑛𝑖 nor any subsequent number can be reached, so
return false.
If 𝑛𝑖+𝑖 is greater than 𝑚𝑎𝑥, set 𝑚𝑎𝑥 to 𝑛𝑖+𝑖.
If 𝑚𝑎𝑥 is greater than or equal to the last index in 𝑁
return true.
Otherwise return false.
Here's a Ruby implementation:
def can_jump(nums)
max_reach = 0
nums.each_with_index do |num, idx|
return false if idx > max_reach
max_reach = [idx+num, max_reach].max
end
max_reach >= nums.size - 1
end
p can_jump([2,3,1,1,4]) # => true
p can_jump([3,2,1,0,4]) # => false
See it on repl.it: https://repl.it/FvlV/1
Your code is O(n^2), but you can produce the result in O(n) time and O(1) space. The idea is to work backwards through the array keeping the minimum index found so far from which you can reach index n-1.
Something like this:
def can_jump(nums)
min_index = nums.length - 1
for i in (nums.length - 2).downto(0)
if nums[i] + i >= min_index
min_index = i
end
end
min_index == 0
end
print can_jump([2, 3, 1, 1, 4]), "\n"
print can_jump([3, 2, 1, 0, 4]), "\n"

I don't understand this method

I'm a beginner in Ruby and I don't understand what this code is doing, could you explain it to me, please?
def a(n)
s = 0
for i in 0..n-1
s += i
end
s
end
def defines a method. Methods can be used to run the same code on different values. For example, lets say you wanted to get the square of a number:
def square(n)
n * n
end
Now I can do that with different values and I don't have to repeat n * n:
square(1) # => 1
square(2) # => 4
square(3) # => 9
= is an assignment.
s = 0 basically says, behind the name s, there is now a zero.
0..n-1 - constructs a range that holds all numbers between 0 and n - 1. For example:
puts (0..3).to_a
# 0
# 1
# 2
# 3
for assigns i each consecutive value of the range. It loops through all values. So first i is 0, then 1, then ... n - 1.
s += i is a shorthand for s = s + i. In other words, increments the existing value of s by i on each iteration.
The s at the end just says that the method (remember the thing we opened with def) will give you back the value of s. In other words - the sum we accumulated so far.
There is your programming lesson in 5 minutes.
This example isn't idiomatic Ruby code even if it is syntactically valid. Ruby hardly ever uses the for construct, iterators are more flexible. This might seem strange if you come from another language background where for is the backbone of many programs.
In any case, the program breaks down to this:
# Define a method called a which takes an argument n
def a(n)
# Assign 0 to the local variable s
s = 0
# For each value i in the range 0 through n minus one...
for i in 0..n-1
# ...add that value to s.
s += i
end
# The result of this method is s, the sum of those values.
s
end
The more Ruby way of expressing this is to use times:
def a(n)
s = 0
# Repeat this block n times, and in each iteration i will represent
# a value in the range 0 to n-1 in order.
n.times do |i|
s += i
end
s
end
That's just addressing the for issue. Already the code is more readable, mind you, where it's n.times do something. The do ... end block represents a chunk of code that's used for each iteration. Ruby blocks might be a little bewildering at first but understanding them is absolutely essential to being effective in Ruby.
Taking this one step further:
def a(n)
# For each element i in the range 0 to n-1...
(0..n-1).reduce |sum, i|
# ...add i to the sum and use that as the sum in the next round.
sum + i
end
end
The reduce method is one of the simple tools in Ruby that's quite potent if used effectively. It allows you to quickly spin through lists of things and compact them down to a single value, hence the name. It's also known as inject which is just an alias for the same thing.
You can also use short-hand for this:
def a(n)
# For each element in the range 0 to n-1, combine them with +
# and return that as the result of this method.
(0..n-1).reduce(&:+)
end
Where here &:+ is shorthand for { |a,b| a + b }, just as &:x would be short for { |a,b| a.x(b) }.
As you are a beginner in Ruby, let's start from the small slices.
0..n-1 => [0, n-1]. E.g. 0..3 => 0, 1, 2, 3 => [0, 3]
for i in 0.. n-1 => this is a for loop. i traverses [0, n-1].
s += i is same as s = s + i
So. Method a(n) initializes s = 0 then in the for loop i traverse [0, n - 1] and s = s + i
At the end of this method there is an s. Ruby omits key words return. so you can see it as return s
def a(n)
s = 0
for i in 0..n-1
s += i
end
s
end
is same as
def a(n)
s = 0
for i in 0..n-1
s = s + i
end
return s
end
a(4) = 0 + 1 + 2 + 3 = 6
Hope this is helpful.
The method a(n) calculates the sums of the first n natural numbers.
Example:
when n=4, then s = 0+1+2+3 = 6
Let's go symbol by symbol!
def a(n)
This is the start of a function definition, and you're defining the function a that takes a single parameter, n - all typical software stuff. Notably, you can define a function on other things, too:
foo = "foo"
def foo.bar
"bar"
end
foo.bar() # "bar"
"foo".bar # NoMethodError
Next line:
s = 0
In this line, you're both declaring the variable s, and setting it's initial value to 0. Also typical programming stuff.
Notably, the value of the entire expression; s = 0, is the value of s after the assignment:
s = 0
r = t = s += 1 # You can think: r = (t = (s += 1) )
# r and t are now 1
Next line:
for i in 0..n-1
This is starting a loop; specifically a for ... in ... loop. This one a little harder to unpack, but the entire statement is basically: "for each integer between 0 and n-1, assign that number to i and then do something". In fact, in Ruby, another way to write this line is:
(0..n-1).each do |i|
This line and your line are exactly the same.
For single line loops, you can use { and } instead of do and end:
(0..n-1).each{|i| s += i }
This line and your for loop are exactly the same.
(0..n-1) is a range. Ranges are super fun! You can use a lot of things to make up a range, particularly, time:
(Time.now..Time.new(2017, 1, 1)) # Now, until Jan 1st in 2017
You can also change the "step size", so that instead of every integer, it's, say, every 1/10:
(0..5).step(0.1).to_a # [0.0, 0.1, 0.2, ...]
Also, you can make the range exclude the last value:
(0..5).to_a # [0, 1, 2, 3, 4, 5]
(0...5).to_a # [0, 1, 2, 3, 4]
Next line!
s += i
Usually read aloud a "plus-equals". It's literally the same as: s = s + 1. AFAIK, almost every operator in Ruby can be paired up this way:
s = 5
s -= 2 # 3
s *= 4 # 12
s /= 2 # 6
s %= 4 # 2
# etc
Final lines (we'll take these as a group):
end
s
end
The "blocks" (groups of code) that are started by def and for need to be ended, that's what you're doing here.
But also!
Everything in Ruby has a value. Every expression has a value (including assignment, as you saw with line 2), and every block of code. The default value of a block is the value of the last expression in that block.
For your function, the last expression is simply s, and so the value of the expression is the value of s, after all is said and done. This is literally the same as:
return s
end
For the loop, it's weirder - it ends up being the evaluated range.
This example may make it clearer:
n = 5
s = 0
x = for i in (0..n-1)
s += i
end
# x is (0..4)
To recap, another way to write you function is:
def a(n)
s = 0
(0..n-1).each{ |i| s = s + i }
return s
end
Questions?

Ruby : Finding lowest free ID in an ID array

I have an array with different IDs going from 1 to 4000. I need to add some elements in a database with an ID that would go in that array. Since the biggest ID possible is 4000 (which is not that much in my case), I'd like to be able to find the lowest unused ID possible I could use for my new element.
I would know how to do that in C++, but since I'm pretty new in Ruby, I'm asking for help. in C++, I would write a loop in which I would check if array[i] == array[i+1] - 1. If not the case, then the new id would be array[i] + 1.
I have just no idea how to write that in Ruby.
Using a range, you can find the first element that is not part of your array:
array = [1,2,3,5,6]
(1..4000).find { |i| !array.include?(i) }
# => 4
array = [1, 2, 3, 5, 6]
(1..4000).to_a.-(array).min
def first_unused_id(ids)
index = ids.each_index.find{|i| ids[i] + 1 != ids[i+1] }
ids[index] + 1
end
Some explanation:
each_index will transform the array into an Enumerator giving the arrays indices.
find will return the first element that returns true from the block passed to it.
how about this one:
(1..4000).find { |i| array[i-1] != i }
similar to Dylan's answer but in this case, it simply checks whether the [n-1]th member of the array is n. If not, that index is "open" and is returned. This solution only requires one check per index, not 4000...
so for
array = [1,2,3,5,6]
this would find that array[4-1] != 4 (because array[3] = 5) and return 4 as the first available id.
(this requires a sorted array of indices but that has been assumed so far)
array = [1, 2, 3, 5, 6]
def lowest_unused(ids)
ids.find { |e| ids.index(e) + 1 != e } - 1
end
p lowest_unused(array) # 4

Resources