Locate a string in an array of strings - ruby

I would like to find the location in an array of all strings of any given word.
phrase = "I am happy to see you happy."
t = phrase.split
location = t.index("happy") # => 2 (returns only first happy)
t.map { |x| x.index("happy") } # => returns [nil, nil, 0, nil, nil, nil, 0]

Here's a way
phrase = "I am happy to see you happy."
t = phrase.split(/[\s\.]/) # split on dot as well, so that we get "happy", not "happy."
happies = t.map.with_index{|s, i| i if s == 'happy'} # => [nil, nil, 2, nil, nil, nil, 6]
happies.compact # => [2, 6]

phrase = "I am happy to see you happy."
phrase.split(/[\W]+/).each_with_index.each_with_object([]) do |obj,res|
res << obj.last if obj.first == "happy"
end
#=> [2, 6]

Related

error when searching through 2d array ruby

I have the following grids (connect four)
grid1 = [
[nil, nil, nil],
[1, nil, nil],
[1, nil, nil],
[1, nil, nil]
]
grid2 = [
[nil, nil, nil],
[nil, nil, 1],
[nil, nil, 1],
[nil, nil, 1]
]
grid3 = [
[nil, nil, nil],
[nil, nil, nil],
[nil, nil, nil],
[1, 1, 1]
]
and this is the method I created to find three 1's in a vertical row and return the next available slot above
def searchArray(array)
array.each_with_index do |y, yi|
y.each_with_index do |x, xi|
if array[yi][xi] != nil && array[yi][xi] == array[yi+1][xi] && array[yi][xi] == array[yi+2][xi]
return v = [yi-1, xi]
end
end
end
end
searchArray(grid2)
When I call the method on grid1, and grid 2 it works great but when I call it on Grid 3 the grid where the 1's are placed on the bottom row I get this error
undefined method `[]' for nil:NilClass
(repl):28:in `block (2 levels) in searchArray'
(repl):27:in `each'
(repl):27:in `each_with_index'
(repl):27:in `block in searchArray'
(repl):26:in `each'
(repl):26:in `each_with_index'
(repl):26:in `searchArray'
(repl):36:in `<main>'
Not sure what's going on
Thanks
You can solve a lot of problems here by simplifying this code using dig:
def search_array(array)
array.each_with_index do |y, yi|
y.each_with_index do |x, xi|
stack = (0..2).map { |o| array.dig(yi + o, xi) }
if (stack == [ 1, 1, 1 ])
return [ yi - 1, xi ]
end
end
end
end
Where dig can poke around and not cause exceptions if it misses the end of the array. Here map is used to quickly pull out an N high stack. You can do 1..2 or 0..4 or whatever is necessary.
Let's take a look at your code, simplified slightly1:
def search_array(array)
array.each_with_index do |y, yi|
y.each_with_index do |x, xi|
return [yi-1, xi] if x != nil && x == array[yi+1][xi] && x == array[yi+2][xi]
end
end
end
You go one row at a time, then for each element in that row, check if that element is not nil and if so, determine whether the two elements below it have the same non-nil value. If you reach the penultimate (next-to-last) row, yi = array.size - 2, you will compare x with array[yi+2][xi], which equals array[array.size][xi], which in turn equals nil[xi]. However, nil has no method [] so an undefined method exception is raised. Pay close attention to those error messages; often, as here, they guide you to the error.
Another problem is that if you found 1's in the first three rows of a column j you would return the index [-1, j], -1 being 0-1. You don't want that either.
I understand that you also wish to determine if dropping a coin in a column results in four-in-a-row horizontally. You could check both vertically and horizontally as follows.
def search_array(arr)
arr.first.each_index do |j|
r = arr.each_index.find { |i| arr[i][j] == 1 }
next if r == 0
r = r.nil? ? arr.size-1 : r-1
return [r,j] if below?(arr,r,j) || left?(arr,r,j) || right?(arr,r,j)
end
nil
end
def below?(arr,r,j)
r < arr.size-3 && (1..3).all? { |i| arr[r+i][j] == 1 }
end
def right?(arr,r,j)
j < arr.first.size-3 && (1..3).all? { |i| arr[r][j+i] == 1 }
end
def left?(arr,r,j)
j >= 3 && (1..3).all? { |i| arr[r][j-i] == 1 }
end
grid4 = [
[nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil],
[nil, nil, 1, nil, nil],
[nil, nil, 1, 1, 1],
[ 1, 1, 1, nil, 1]
]
grid5 = [
[nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil],
[nil, nil, 1, nil, nil],
[nil, 1, 1, nil, nil],
[nil, 1, 1, nil, 1]
]
search_array grid1 #=> [0, 0] (vertical)
search_array grid2 #=> [0, 2] (vertical)
search_array grid3 #=> nil
search_array grid4 #=> [3, 1] (horizontal)
search_array grid5 #=> [1, 2] (vertical)
Note that if you wish to also check for four-in-a-row diagonnal you could change:
return [r,j] if below?(arr,r,j) || left?(arr,r,j) || right?(arr,r,j)
to
return [r,j] if below?(arr,r,j) || left?(arr,r,j) || right?(arr,r,j) ||
top_left_to_bottom_right?(arr,r,j) || bottom_left_to_top_right?(arr,r,j)
and add the additional methods top_left_to_bottom_right? and bottom_left_to_top_right?.
1. I changed the name of your method to search_array because Ruby has a convention to use snake case for the naming of variables and methods. You don't have to adopt that convention but 99%+ of Rubiests do.
I could suggest a slight different approach, this is not a complete solution, just a start. It should also help to catch the four.
First map the not nil indexes of the grid, let's consider grid3:
mapping = grid3.flat_map.with_index{ |y, yi| y.map.with_index { |x, xi| [xi, yi] if x }.compact }
#=> [[0, 3], [1, 3], [2, 3]]
Then group by first and second element to get the columns and rows:
cols = mapping.group_by(&:first) #=> {0=>[[0, 3]], 1=>[[1, 3]], 2=>[[2, 3]]}
rows = mapping.group_by(&:last) #=> {3=>[[0, 3], [1, 3], [2, 3]]}
Now, if you want to look for three elements in a row or in a column:
cols.keep_if { |_,v| v.size == 3 } #=> {}
rows.keep_if { |_,v| v.size == 3 } #=> {3=>[[0, 3], [1, 3], [2, 3]]}
The first line says there are no columns with three element aligned.
The second line says that row with index 3 has three elements aligned and indexes are [[0, 3], [1, 3], [2, 3]].
Next step it to check that there are no gaps amongst elements. For example in a 4x4 grid you could get also [[0, 3], [1, 3], [3, 3]] which are three elements, but there is a gap in [2, 3],

Ruby - creating a grid

This is very basic, but can someone explain in plain in english what exactly is happening in this code?
3.times do |row_index|
board[row_index] = []
3.times do |column_index|
board[column_index] = []
board[row_index][column_index] = nil
end
end
end
I will first correct your code and then will show you how to improve it with increasing simplifications.
Presumably the array board is initialized before your code and, because of the extra end is probably in a method, we need:
def initialize_board(n, val)
board = Array.new(n)
n.times do |row_index|
board[row_index] = Array.new(n)
n.times do |column_index|
board[row_index][column_index] = val
end
end
board
end
initialize_board(3, nil)
#=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
You see that, to make the method more robust, I've made the board's size (n) and initialization value (val) variables. The method must return board, so we need board as the next-to-last line. (Because it is the last line of the method that is executed, return board is not needed.)
Firstly, since you have board[row_index] =..., board must be created as an array with n elements. That's what Array.new(n) does. Similarly, since you have board[row_index][column_index] =..., board[row_index] (for each value of row_index) must be created as an array with n elements:
board[row_index] = Array.new(n)
This works, but it's not very Ruby-like. Better would be to write:
def initialize_board(n, val)
board = []
n.times do |row_index|
row = []
n.times { |column_index| row << val } # or row.push(val)
board << row # or board.push(val)
end
board
end
initialize_board(3, nil)
#=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
Notice that board is initialized to an empty array, is then filled with rows, then board is returned. Similarly, row is initialized to an empty array, filled with copies of val and then appended to board. We can tighten that up by using Enumerable#each_with_object:
def initialize_board(n, val)
n.times.with_object([]) do |row_index, board|
board << n.times.with_object([]) { |column_index, row| row << val }
end
end
initialize_board(3, nil)
#=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
We can now use Array.new with a block to create each row with the default value:
def initialize_board(n, val)
n.times.with_object([]) do |row_index, board|
board << Array.new(n) { val }
end
end
initialize_board(3, nil)
#=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
and then do it again:
def initialize_board(n, val)
Array.new(n) { Array.new(n) { val } }
end
arr = initialize_board(3, nil)
#=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
There's one last thing I'd like to mention. Suppose we set:
arr[1][1] = 'cat'
Then
arr #=> [[nil, nil, nil], [nil, "cat", nil], [nil, nil, nil]]
as expected.
If, however, we had written:
def initialize_board(n, val)
Array.new(n, Array.new(n, val))
end
Then:
arr = initialize_board(3, nil)
#=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
arr[1][1] = 'cat'
arr #=> [[nil, "cat", nil], [nil, "cat", nil], [nil, "cat", nil]]
which clearly is not what you want.
Try this way in Ruby
Input n defined as size of n x n matrix
Example
if n = 3
Matrix size is 3 x 3
Code
n = gets.chomp.to_i
# Array('A'..'Z').sample is random value from A to Z
matrix = Array.new(n) { Array.new(n) { Array('A'..'Z').sample } }
Output
[["D", "A", "M", "V"], ["X", "Q", "A", "E"], ["P", "D", "L", "S"],
["V", "M", "P", "Z"]]

sum of corresponding elements of array including nil

How can I sum in Ruby
[1, 2, nil, 4]
with
[nil, 2, nil, 4]
and have
[1, 4, nil, 8]
?
For example as follows:
a = [1, 2, nil, 4]
b = [nil, 2, nil, 4]
a.map.with_index {|v,i| (v || b[i]) && v.to_i + b[i].to_i }
More estetic way is:
a.zip(b).map {|v| v.compact.reduce(:+) }
Explanation: Here #zip just reconstructs the array with other passed value-by-value, so in 1st row will consist of 1st elements of each array, 2nd of 2nds, etc. Then #compact, and #reduce is applied of each of rows 1st, 2nd, etc. #compact just removes nil value, so they do not included in result. #reduce then construct a sum for each row, via :+ operator between sum (by default it is nil), and value, so in output it resulted in the sum of values, of nil in case empty row.
If we'll get the more general approach, let's sum rows of the matrix.
m = [ [ 1, 2, nil, 4],
[nil, 2, nil, 4] ]
m.shift.zip(*m).map {|v| v.compact.reduce(:+) }
a.zip(b).map {|x, y| x.nil? ? (y.nil? ? nil : y) : (y.nil? ? x : x + y)}
# => [1, 4, nil, 8]
Here's a way that makes use of the fact that nil.to_i => O:
a.zip(b).map { |e,f| [e,f]==[nil,nil] ? nil : e.to_i + f.to_i }

Ruby check array, return the indexes, which data is exist

How can I check if there's a data that not nil in an array, and then return the index of that data?
Example:
myary = [nil, nil, 300, nil, nil] # <= index 2 is 300
now is there a method to get the value 2? As we know the index 2 is 300 and not nil.
I need to get the index not the value. And moreover there probably will ot only one element that is not nil, perhaps the array could be like this
myotherary = [nil, nil, 300, 400, nil] # <= index 2,3 = 300,400
now for this I need to get 2 and 3 value, is this posibble?
Okay thank you very much, I appreciate all answer.
P.S : Please no flaming, if you don't want to help then just leave, I have spent some time to solve this matter and not succeed. I'm not going to ask here if I can solve it by myself. I had enough of them who not helping, instead asking "what method have you tried?" or write something else that actually not helping but harrasing.
You can use map.with_index:
myary.map.with_index { |v, i| i if v }.compact
# => [2]
myotherary.map.with_index { |v, i| i if v }.compact
# => [2, 3]
I would be inclined to use Enumerable#select in part because it reads well; the word "select" describes what you want to do.
Code
For just the indices:
def indices_only(arr)
arr.size.times.select { |i| arr[i] }
end
If it would be more useful to return both non-nil values and corresponding indices:
def values_and_indices(arr)
arr.each_with_index.select(&:first)
end
Examples
arr1 = [nil, nil, 300, nil, nil]
arr2 = [nil, nil, 300, 400, nil]
indices_only(arr1) #=> [2]
indices_only(arr2) #=> [2, 3]
values_and_indices(arr1) #=> [[300, 2]]
values_and_indices(arr2) #=> [[300, 2], [400, 3]]

How do I iterate through an array of nils?

I'm trying to set up an Array with all nil values so that someone can iterate the sequence for each value until it reaches the end, then displays the changed array.
class Big
def ben
x = [nil,1,nil,2]
y = 0
x[y] == nil ? "good": "bad"
y += 1
puts x
end
end
I know this can be simplified. Is there a way to overwrite each value in the array?
Here are some things that might help, based on what I see in the sample code.
This is a simple way to create an array if you want it to be a certain size filled with nil values:
foo = [nil] * 5
=> [nil, nil, nil, nil, nil]
If you want to interweave two arrays, such as an array of nils and another one with values:
TOTAL_ELEMENTS = 5
([nil] * TOTAL_ELEMENTS).zip((1..TOTAL_ELEMENTS).to_a).flatten
=> [nil, 1, nil, 2, nil, 3, nil, 4, nil, 5]
Based on the OPs comment below, that this is for a tic-tac-toe game, here are some ways to create x:
Array.new(9)
[nil] * 9
Both of which return:
=> [nil, nil, nil, nil, nil, nil, nil, nil, nil]
That is useful if you receive the cell coordinate as an offset from 0.
For a tic-tac-toe grid it might be more useful to have three rows of three columns if you get your cell coordinates as an row/column pair:
Array.new(3) { Array.new(3) }
[[nil] * 3] * 3
Returning:
=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
And some things to meditate on:
ROWS = COLUMNS = 3
x = Array.new( ROWS * COLUMNS ) # for offsets
x = Array.new(ROWS) { Array.new(COLUMNS) } # for rows and columns
If you get your position as an offset but want to convert it to a row/column,
use divmod. Your offset will be 0..8, being converted to fit into a 3x3 grid,
i.e. [0..2][0..2]. Converting back is easy too:
def row_col_to_offset(x,y)
x * ROW + y
end
>> row_col_to_offset(0,0) # => 0
>> row_col_to_offset(0,1) # => 1
>> row_col_to_offset(1,1) # => 4
>> row_col_to_offset(2,2) # => 8
def offset_to_row_col(o)
o.divmod(ROW)
end
>> offset_to_row_col(0) # => [0, 0]
>> offset_to_row_col(1) # => [0, 1]
>> offset_to_row_col(4) # => [1, 1]
>> offset_to_row_col(8) # => [2, 2]
Now you need to learn about Ruby's # instance variables, and the proper use of the initialize method.
If I'm understanding you right you could do this:
class Big
def ben
x = [nil,1,nil,2]
# These lines don't do much
# y = 0
# x[y] == nil ? "good": "bad"
# y += 1
puts x
end
end
And yes to your second question: fill. For example:
[1, 2, 3].fill(0) # => [0, 0, 0]

Resources