Ruby - 2 dimension array search - ruby

I have the following 2 dimension array
data = [
[5014031, nil], [5014032, nil], [5014033, 0],
[5014034, nil], [3014035, 1], [5014036, 1],
[5014037, 2], [5014038, nil], [5014039, 2],
[5014040, nil], [2014041, nil], [3014042, 2]
]
When I know the value of the 1st integer of one of these arrays e.g. 5014034, what would be the most efficient way to gain the next integer value where the 2nd element matches to nil? (e.g. I would expect 5014038 to be returned)
Thanks
Scott

Simple way, using #drop_while and #find_index:
data = [
[5014031, nil], [5014032, nil], [5014033, 0],
[5014034, nil], [5014035, 1 ], [5014036, 1],
[5014037, 2 ], [5014038, nil], [5014039, 2],
[5014040, nil], [5014041, nil], [5014042, 2]
]
remaining_arr = data.drop_while { |arr| arr[0] != 5014034 }[1..]
next_int = remaining_arr[remaining_arr.find_index { |arr| arr[1].nil? }][0]
Alternatively:
data
.drop_while { |x| x[0] != 5014034 }[1..]
.drop_while { |x| !x[1].nil? }[0][0]
# => 5014038

You could slice the array into two when the first element in the inner array matches 5014034. Extract the second resulting array and use find to look for the inner array whose second element is nil:
arr
.slice_when { |a, _| a == 5014034 } # #<Enumerator: ...>
.to_a # [[[5014031, nil], [5014032, nil], [5014033, 0], [5014034, nil]], [[5014035, 1], [5014036, 1], [5014037, 2], [5014038, nil], [5014039, 2], [5014040, nil], [5014041, nil], [5014042, 2]]]
[1] # [[5014035, 1], [5014036, 1], [5014037, 2], [5014038, nil], [5014039, 2], [5014040, nil], [5014041, nil], [5014042, 2]]
.find { |_, b| b.nil? } # [5014038, nil]
[0] # 5014038
Notice this method chaining might fail if there's no array in arr whose first object inside matches 5014034, and/or the same for find.

If sorted then the following might be faster
data = [
[5014031, nil], [5014032, nil], [5014033, 0],
[5014034, nil], [5014035, 1], [5014036, 1],
[5014037, 2], [5014038, nil], [5014039, 2],
[5014040, nil], [5014041, nil], [5014042, 2]
]
start_element = data.bsearch_index{ |a| a[0] >= 5014034 }
if start_element == nil || data[start_element] != 5014034
puts "not found"
else
data.slice((start_element+1)..).find { |a| a[1] == nil }[0]
end

Related

How to replace this nil value in Ruby style

I have an array like
A=[[a,x,y,z],[nil,e,f,d],[nil,k,j,u],[nil,w,p,k],[b,x,y,z],[nil,e,f,d],[nil,k,j,u],[nil,w,p,k]]
Result
A=[[a,x,y,z],[a,e,f,d],[a,k,j,u],[a,w,p,k],[b,x,y,z],[b,e,f,d],[b,k,j,u],[b,w,p,k]]
You might be noticing that first character of the first array is replacing the upcoming nil value until first character of the first array is b and then nil values are replaced with b.
This one I have achieved through iterating the array and writing the if condition and storing the first character somewhere and then by comparing the first character while I am iterating. But I don't still don't find a ruby way of doing this. Can someone help me here
A = [['a',1,2],[nil,3,4],[nil,5,6],[nil,7,8],['b',9,10],[nil,11,12],[nil,13,14]]
A.each_cons(2){|first, last| last[0] ||= first[0] }
last[0] ||= first[0] means something like "Give last[0] the value of first[0], unless it already has a value (not nil or false)".
arr = [['a',1,2],[nil,3,4],[nil,5,6],[nil,7,8],['b',9,10],[nil,11,12],[nil,13,14]]
last = nil
arr.map do |f,*rest|
if f.nil?
[last, *rest]
else
last = f
[f, *rest]
end
end
#=> [["a", 1, 2], ["a", 3, 4], ["a", 5, 6], ["a", 7, 8],
# ["b", 9, 10], ["b", 11, 12], ["b", 13, 14]]
The steps are as follows:
last = nil
enum = arr.map
#=> #<Enumerator: [["a", 1, 2], [nil, 3, 4], [nil, 5, 6], [nil, 7, 8],
# ["b", 9, 10], [nil, 11, 12], [nil, 13, 14]]:map>
f,*rest = enum.next
#=> ["a", 1, 2]
f #=> "a"
rest
#=> [1, 2]
f.nil?
#=> false
last = f
#=> "a"
[f, *rest]
#=> ["a", 1, 2]
f,*rest = enum.next
#=> [nil, 3, 4]
f.nil?
#=> true
[last, *rest]
#=> ["a", 3, 4]
f,*rest = enum.next
#=> [nil, 5, 6]
f.nil?
#=> true
[last, *rest]
#=> ["a", 5, 6]
f,*rest = enum.next
#=> [nil, 7, 8]
f.nil?
#=> true
[last, *rest]
#=> ["a", 7, 8]
and so on.
Alternatively,
arr.drop(1).each_with_object([arr.first]) { |a,ar|
ar << [a.first || ar.last.first, *a.drop(1)] }
#=> [["a", 1, 2], ["a", 3, 4], ["a", 5, 6], ["a", 7, 8],
# ["b", 9, 10], ["b", 11, 12], ["b", 13, 14]]

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],

translate java 2d array into ruby

How would I write this java code into ruby:
String[] [] Score = new String [row] [col];
Score[rCount][cCount] = num;
I thought it would as simple as:
score=[]
score[rcount][ccount]=num
But I keep getting "undefined method `[]=' for nil:NilClass (NoMethodError)"
Sorry, I don't know java, but have a look at the class methods Array#new and Array::[], and the instance methods Array#[]= and Array#[]. Here are some examples that should answer your question (and other questions that may be sparked, hopefully):
Array.new #=> []
[] #=> [] # shorthand for above
a = Array.new(5) { [] } #=> [[], [], [], [], []]
a[0][0] = 2
a #=> [[2], [], [], [], []]
a[3][2] = 4
a #=> [[2], [], [], [nil, nil, 4], []]
a[1] << 1
a #=> [[2], [1], [], [nil, nil, 4], []]
a[1] << 2
a #=> [[2], [1, 2], [], [nil, nil, 4], []]
a[1] << 3 << 4
a #=> [[2], [1, 2, 3, 4], [], [nil, nil, 4], []]
a[2] << [4,5]
a #=> [[2], [1, 2, 3, 4], [[4, 5]], [nil, nil, 4], []]
a[4].concat([4,5])
a #=> [[2], [1, 2, 3, 4], [[4, 5]], [nil, nil, 4], [4, 5]]
a = Array.new(3) { Array.new(3) }
#=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
a[1][2] = 4
a #=> [[nil, nil, nil], [nil, nil, 4], [nil, nil, nil]]
We could also write the default as a second argument:
a = Array.new(3,[]) #=> [[], [], []]
but that can be problematic:
a[0][0] = 'cat'
a #=> [["cat"], ["cat"], ["cat"]]
as is:
a = Array.new(3,Array.new(2)) #=> [[], [], []]
#=> [[nil, nil], [nil, nil], [nil, nil]]
a[0][0] = 'cat'
a #=> [["cat", nil], ["cat", nil], ["cat", nil]]
since each element of a is the same array.
Note that Ruby provides a convenience for writing certain methods that is commonly referred to as "syntactic sugar". If, for example, you write a = [1,2,3], Ruby will interpret that as a = Array.[](1,2,3) (and you could write it that way), the class method being Array::[]. Similarly, if a equals [1,2,3], a[1] = 'cat' is decoded as a.[]=(1, 'cat') and a[1] #=> 'cat' is a.[](1). Similarly, h = {} translates to h = Hash.new and so on.
Note that Ruby does not have a concept of "multidimensional arrays". For more on that you may wish to see a comment a left on this question.
Firstly, ruby programmers use snake case. Capital letter is using for class names.
Secondly, your problem happens just because
score[rcount] == nil # true
If you want to have an access to second dimension elements you need to initialize line as array:
score[rcount] = []
Now you can set second dimension element
score[rcount][ccount] = num

Ruby extract subarray from integer

I need to extract part of an array based on an integer and if there are no enough values, fill this array with specifics values if the array size doesn't feet with this integer.
As example :
I have an array like that:
[[1,2], [2,1], [3,3]]
If my integer is 2 I need this :
[[1,2], [2,1]]
If my integer is 4 I need this :
[[1,2], [2,1], [3,3], [nil, nil]]
You can do the same using Fixnum#times methos:
a = [[1,2], [2,1], [3,3]]
def extract_sub_array array, size
size.times.map { |i| array.fetch(i, [nil, nil]) }
end
extract_sub_array a, 2
# => [[1, 2], [2, 1]]
extract_sub_array a, 4
# => [[1, 2], [2, 1], [3, 3], [nil, nil]]
def convert(a,n)
Array.new(n) { |i| a[i] || [nil,nil] }
end
a = [[1,2], [2,1], [3,3]]
convert(a,2) #=> [[1, 2], [2, 1]]
convert(a,3) #=> [[1, 2], [2, 1], [3, 3]]
convert(a,4) #=> [[1, 2], [2, 1], [3, 3], [nil, nil]]
convert(a,5) #=> [[1, 2], [2, 1], [3, 3], [nil, nil], [nil, nil]]
Assuming, the desired_length is specified and an array is named arr:
arr = [[1,2], [2,1], [3,3]]
# Will shrink an array or fill it with nils
# #param arr [Array] an array
# #param desired_length [Integer] the target length
def yo arr, desired_length
arr[0...desired_length] + [[nil,nil]]*[0,desired_length-arr.length].max
end
yo arr, 2
#⇒ [[1,2], [2,1]]
yo arr, 4
#⇒ [[1,2], [2,1], [3,3], [nil, nil]]

How do I implement Common Lisp's mapcar in Ruby?

I want to implement Lisp's mapcar in Ruby.
Wishful syntax:
mul = -> (*args) { args.reduce(:*) }
mapcar(mul, [1,2,3], [4,5], [6]) would yield [24, nil, nil].
Here is the solution I could think of:
arrs[0].zip(arrs[1], arrs[2]) => [[1, 4, 6], [2, 5, nil], [3, nil, nil]]
Then I could:
[[1, 4, 6], [2, 5, nil], [3, nil, nil]].map do |e|
e.reduce(&mul) unless e.include?(nil)
end
=> [24, nil, nil]
But I'm stuck on the zip part. If the input is [[1], [1,2], [1,2,3], [1,2,3,4]], the zip part would need to change to:
arrs[0].zip(arrs[1], arrs[2], arrs[3])
For two input arrays I could write something like this:
def mapcar2(fn, *arrs)
return [] if arrs.empty? or arrs.include? []
arrs[0].zip(arrs[1]).map do |e|
e.reduce(&fn) unless e.include? nil
end.compact
end
But I do not know how go beyond more than two arrays:
def mapcar(fn, *arrs)
# Do not know how to abstract this
# zipped = arrs[0].zip(arrs[1], arrs[2]..., arrs[n-1])
# where n is the size of arrs
zipped.map do |e|
e.reduce(&fn) unless e.include?(nil)
end.compact
end
Does anyone have any advice?
If I got your question properly you just need:
arrs = [[1,2], [3,4], [5,6]]
zipped = arrs[0].zip(*arrs[1..-1])
# => [[1, 3, 5], [2, 4, 6]]
Or a nicer alternative, IHMO:
zipped = arrs.first.zip(*arrs.drop(1))
If all arrays inside arrs are of the same length you can use the transpose method:
arrs = [[1,2], [3,4], [5,6]]
arrs.transpose
# => [[1, 3, 5], [2, 4, 6]]
According to toro2k, one of the possible implementations of mapcar in Ruby:
def mapcar(fn, *arrs)
return [] if arrs.empty? or arrs.include? []
transposed = if arrs.all? { |a| arrs.first.size == a.size }
arrs.transpose
else
arrs[0].zip(*arrs.drop(1))
end
transposed.map do |e|
e.collect(&fn) unless e.include? nil
end.compact!
end

Resources