How do I find .index of a multidimensional array - ruby

Tried web resources and didnt have any luck and my visual quick start guide.
If I have my 2d/multidimensional array:
array = [['x', 'x',' x','x'],
['x', 'S',' ','x'],
['x', 'x',' x','x']]
print array.index('S')
it returns nil
So then I go and type:
array = ['x', 'S',' ','x']
print array.index('S')
it returns the value I am looking for 1
My first guess something is being called wrong in the .index() and it needs two arguments one for both row and column? Anyways how do I make .index work for a multidimensional array? This is step one for solving my little maze problem

This will do it:
array = [['x', 'x',' x','x'],
['x', 'S',' ','x'],
['x', 'x',' x','x']]
p array.index(array.detect{|aa| aa.include?('S')}) # prints 1
If you also want 'S's index in the sub array you could:
row = array.detect{|aa| aa.include?('S')}
p [row.index('S'), array.index(row)] # prints [1,1]

You can use the method Matrix#index:
require 'matrix'
Matrix[*array].index("S")
#=> [1, 1]

a.each_index { |i| j = a[i].index 'S'; p [i, j] if j }
Update: OK, we can return multiple matches. It's probably best to utilize the core API as much as possible, rather than iterate one by one with interpreted Ruby code, so let's add some short-circuit exits and iterative evals to break the row into pieces. This time it's organized as an instance method on Array, and it returns an array of [row,col] subarrays.
a = [ %w{ a b c d },
%w{ S },
%w{ S S S x y z },
%w{ S S S S S S },
%w{ x y z S },
%w{ x y S a b },
%w{ x },
%w{ } ]
class Array
def locate2d test
r = []
each_index do |i|
row, j0 = self[i], 0
while row.include? test
if j = (row.index test)
r << [i, j0 + j]
j += 1
j0 += j
row = row.drop j
end
end
end
r
end
end
p a.locate2d 'S'

You could find first in which is the absolute position by flattening the array:
pos = array.flatten.index('S')
Then get the number of columns per row:
ncols = array.first.size
then
row = pos / ncols
col = pos % ncols

Non-Ruby specific answer: You're trying to print 'S' in both examples, but only the latter has 'S' in the array. The first has ['x', 'S', ' ', 'x']. What you will need to do (If Ruby doesn't do this for you) is look at each member in the array and search that member for 'S'. If 'S' is contained in that member then print it.

array = [['x', 'x',' x','x'],
['x', 'S',' ','x'],
['x', 'x',' x','x']]
class Array
def my_index item
self.each_with_index{|raw, i| return i if raw.include? item}
return
end
end
p array.my_index("S") #=>1
p array.my_index("Not Exist Item") #=> nil

Specifies both indexes of the first occurrence of element for one pass on subarrays
a = [[...],[...],[...],[...]]
element = 'S'
result_i = result_j = nil
a.each_with_index do|row, i|
if (j = row.index(element))
result_i, result_j = i, j
break
end
end

Related

Ruby code looping infinitely

class Triplet
def initialize(array,sum)
#array = array.sort()
#array_size = array.size()
#sum = sum
#result = []
end
def get_triplet
#array[0..-3].each_with_index do |arr, ind|
pointer_one = ind + 1
pointer_two = #array_size - 1
while (pointer_one < pointer_two)
temp_sum = #array[pointer_one] + #array[pointer_two] + arr
if(temp_sum == #sum)
#result.push([#array[pointer_one], #array[pointer_two], arr])
elsif temp_sum < #sum
pointer_one = pointer_one +1
else
pointer_two = pointer_two -1
end
end
end
end
def get_result
#result.each do |res|
puts res
end
end
end
puts "Enter the array of numbers"
array = gets.chomp
array = array.split(' ')
array_integer = array.map{|a| a.to_i}
puts array_integer
puts "Enter the sum"
sum = gets.chomp
puts sum
t1 = Triplet.new(array_integer,sum.to_i)
t1.get_triplet
t1.get_result
Can anyone suggest me the fix so that it doesn't loop infinitly. It is program to find triplet in array whose sum is #sum. Its looping in get_triplet method. Initialize method sets the array,array size. get_triplet method should store all three number whose sum is #sum in result array.
Usually a tangle of code like this is a sign something's not right, and in this case the source of the problem is not knowing about the combination method. Here's a functionally equivalent solution:
def triplet(list, target)
list.combination(3).find do |a,b,c|
a + b + c == target
end
end
For example:
arr = [ 1, 2, 3, 4, 5, 6, 7, 8 ]
p triplet(arr, 6)
# => [1, 2, 3]
p triplet(arr, 4)
# => nil
p triplet(arr, 10)
# => [1, 2, 7]
The algorithm used in your code looks problematic, or at least implemented incorrectly, and is also strictly limited to triplets. This code is far more generic and uses a proven, tested algorithm, so it's probably better suited to solving your particular problem.

Intersection of two dimensional array

Is there a simple way to find the intersection of a two dimensional array? For example:
arr1 = [1,2,3,4,5]
arr2 = [5,6,7,8]
arr3 = [5]
bigarr = [arr1,arr1,arr3]
I know that it's possible to do:
intersection = arr1 & arr2 & arr3 # => 5
intersection = big_arr[0] & big_arr[1] & big_arr[2] # => 5
but the number of elements in big_arr will vary. I was wondering if there was a simple way to intersect all the elements in big_arr regardless of the number of elements.
Use #reduce like
arr1 = [1,2,3,4,5]
arr2 = [5,6,7,8]
arr3 = [5]
bigarr = [arr1,arr2,arr3]
bigarr.reduce(:&) # => [5]
What do you want: a method with a pretty face or one that is first to finish line? My friend #Arup has supplied one; I'll offer the another.
Code
def heavy_lifter(a)
wee_one = a.min_by(&:size)
return [] if wee_one.empty?
wee_loc = a.index(wee_one)
counts = wee_one.each_with_object({}) { |e,h| h.update(e=>1) }
nbr_reqd = 1
a.each_with_index do |b,i|
next if i == wee_loc
b.each do |e|
cnt = counts[e]
case
when cnt.nil?
next
when cnt == nbr_reqd
counts[e] = cnt + 1
when cnt < nbr_reqd
counts.delete(e)
return [] if counts.empty?
end
end
nbr_reqd += 1
end
counts.keys.each { |k| counts.delete(k) if counts[k] < nbr_reqd }
counts.keys
end
Example
a1 = [1,2,3,4,5]
a2 = [5,6,7,8]
a3 = [5]
a = [a1,a2,a3]
heavy_lifter(a)
#=> [5]
Explanation
Here's how the method works:
select the smallest array (wee_one). To simplify the explanation, assume it is the first element of a.
convert wee_one to a counting hash, counts, where counts[e] = 1 for each element of wee_one.
iterate through the remaining arrays.
keys of counts will be removed as arrays are processed.
after all calculations are complete, counts.keys equals the intersection of all arrays.
after nbr_reqd arrays have been processed (including wee_one), counts[k] equals the number of those arrays that have been found to contain k. Obviously, if counts[k] < nbr_reqd, key k can be removed from counts (but we will not remove such keys until our attention is drawn to them, or at the end).
suppose we are now to process the array b at offset nbr_reqd, meaning nbr_reqd arrays have been processed (including wee_one at offset zero). For each element e of b, we obtain cnt = counts[e]. There are four possibilities:
cnt == nil, in which case there is nothing to be done;
cnt < nbr_reqd, in which case key e is removed from counts;
cnt == nbr_reqd, meaning e has been present in all previous arrays processed, in which case we execute counts[k] = cnt + 1; and
cnt == nbr_read+1, meaning e has been present in all previous arrays processed and is a duplicate of another e in b that has already been processed, in which case nothing is to be done.
nbr_reqd is incremented by one and the process is repeated for the next array.
after all arrays have been processed, all that remains is to remove each key k in counts for which counts[k] < nbr_reqd.
Cutie method
def cutie(a)
a.reduce(:&)
end
Test data
def test(mx, *sizes)
sizes.map { |sz| Array.new(sz) { rand(mx) } }
end
For example:
test(10,5,6,7)
#=> [[9, 1, 5, 1, 1], [0, 8, 7, 8, 5, 0], [5, 1, 7, 6, 7, 9, 5]]
Benchmark code
require 'benchmark'
def bench(tst)
Benchmark.bm(12) do |bm|
bm.report 'cutie' do
cutie(tst)
end
bm.report 'heavy_lifter' do
heavy_lifter(tst)
end
end
end
Benchmark results
tst = test(1_000_000, 400_000, 600_000, 800_000)
cutie(tst).size
#=> 81929
cutie(tst).sort == heavy_lifter(tst).size
#=> true
bench(tst)
user system total real
cutie 1.610000 0.030000 1.640000 ( 1.639736)
heavy_lifter 1.800000 0.020000 1.820000 ( 1.824281)
sizes = (700_000..890_000).step(10_000).to_a
#=> [700000, 710000, 720000, 730000, 740000,
# 750000, 760000, 770000, 780000, 790000,
# 800000, 810000, 820000, 830000, 840000,
# 850000, 860000, 870000, 880000, 890000]
tst = test(1_000_000, *sizes)
bench(tst)
user system total real
cutie 14.090000 0.440000 14.530000 ( 14.679101)
heavy_lifter 5.830000 0.030000 5.860000 ( 5.935438)

How to find the next element given a certain element in array

I wrote a method to get the next element after a given element inside an array. If I provide the method with c, I want it to return e; if e, I want it to return a, etc.:
array = %w[a f c e]
def find_element_after(element, array)
index = array.find_index(element) + 1
array.at(index)
end
array.find_element_after("c", array)
If I pass in the last element I will get nil. But I want to return the first element instead.
I can solve this with if and else. But I want to know if Ruby has better way?
You could modify your method, taking array size into account, like this:
array = %w[a f c e]
def find_element_after(element, array)
index = array.find_index(element) + 1
array.at(index % array.size) # divide calculated index modulo array size
end
find_element_after('e', array)
# => "a"
If you want to make your method proof to passing argument that isn't member of array, you could do:
def find_element_after(element, array)
index = array.find_index(element)
array.at((index + 1) % array.size) if index
end
find_element_after('u', array)
# => nil
or:
def find_element_after(element, array)
return nil unless array.include?(element)
index = array.find_index(element)
array.at(index % array.size)
end
as you see, there's many possible solutions. Feel free to experiment.
If you pass in the last element, it actually works. The index gets evaluated to the last index, and retrieving the element at lastindex + 1 from the array returns nil.
The problem is when you provide an element that is not present in the array. It's this scenario that will result in the index being nil and then throwing the NoMethodError when you call + 1 on it.
To fix this case, define your method like this:
def find_element_after(element, array)
index = array.find_index(element)
array.at(index + 1) if index
end
Here's a demo showing how it works now (run online):
array = %w[a f c e]
def find_element_after(element, array)
index = array.find_index(element)
array.at(index + 1) if index
end
p find_element_after("c", array) # element in the middle - prints "e"
p find_element_after("e", array) # last element - prints "nil"
p find_element_after("z", array) # element not present in the array - prints "nil" (no error)
You can use each_cons to iterate the array using pairs:
def find_element_after(element, array)
cons = array.each_cons(2).find { |i1, i2| i1 == element }
cons.nil? ? array.first : cons.last
end
find_element_after('c', array)
# => "e"
find_element_after('e', array)
# => "a"
Here are some other ways to do that.
#1 Use the method Array#rotate
def nxt(arr, e)
arr.rotate(arr.index(e)+1).first
end
arr = %w[a f c e]
nxt(arr, 'a') #=> "f"
nxt(arr, 'f') #=> "c"
nxt(arr, 'c') #=> "e"
nxt(arr, 'e') #=> "a"
I think this reads well, but it has the disadvantage of creating a temporary array the size of arr.
#2 Use Array#rotate to construct a hash
h = Hash[arr.zip(arr.rotate(1))]
#=> {"a"=>"f", "f"=>"c", "c"=>"e", "e"=>"a"}
h['a'] #=> "f"
h['f'] #=> "c"
h['c'] #=> "e"
h['e'] #=> "a"
#3 Use Array#cycle to create an enumerator
enum = arr.cycle
def nxt(enum, v)
until enum.next == v do
end
enum.next
end
nxt(enum, 'a') #=> "f"
nxt(enum, 'f') #=> "c"
nxt(enum, 'c') #=> "e"
nxt(enum, 'e') #=> "a"
The latter two approaches should be relatively efficient if you had several values to map.
If need a cycle so to get the first element if the last passed
def find_after(element, array)
idx = array.index(element)
array[idx - array.length + 1]
end

Compare sums of elements in an array: Ruby

I need to check whether the sum of any 2 elements of an array equals to the given number. This is what I came up with, but it doesn't seem to do the comparison
def sum_comparison(int_array, x)
n = int_array.length
(0..n).each do |i|
(1..n).each do |j|
if ((int_array[i].to_i + int_array[j].to_i) == x)
return true
else
return false
end
end
end
end
Your solution seems overly complicated and strongly influenced by the programming style of low-level procedural languages like C. One apparent problem is that you write
n = int_array.length
(0..n).each do |i|
# use int_array[i].to_i inside the loop
end
Now inside the each loop, you will get the numbers i = 0, 1, 2, ..., n, for example for int_array = [3,4,5] you get i = 0, 1, 2, 3. Notice that there are four elements, because you started counting at zero (this is called an off by one error). This will eventually lead to an array access at n, which is one beyond the end of the array. This will again result in a nil coming back, which is probably why you use to_i to convert that back to an integer, because otherwise you would get a TypeError: nil can't be coerced into Fixnum whend doing the addition. What you probably wanted instead was simply:
int_array.each do |i|
# use i inside the loop
end
For the example array [3,4,5] this would actually result in i = 3, 4, 5. To get the combinations of an array in a more Ruby way, you can for example use Array#combination. Likewise, you can use Array#any? to detect if any of the combinations satisfy the specified condition:
def sum_comparison(array, x)
array.combination(2).any? do |a, b|
a + b == x
end
end
When your function compare first element, it's immediately returns false. You need to return only true when iterating and return false at the end if nothing were found, to avoid this issue:
def sum_comparison(int_array, x)
n = int_array.size
(0...n).each do |i|
(1...n).each do |j|
if (int_array[i].to_i + int_array[j].to_i) == x
return true
end
end
end
false
end
To simplify this you can use permutation or combination and any? methods as #p11y suggests. To get founded elements you could use find or detect.
def sum_comparison(a, x)
a.combination(2).any? { |i, j| i + j == x }
end
a.combination(2).detect { |i, j| i + j == x }
# sum_comparison([1,2,3, 4], 6) => [2, 4]
Using an enumerator:
#!/usr/bin/env ruby
def sum_comparison(int_array, x)
enum = int_array.to_enum
loop do
n = enum.next
enum.peek_values.each do |m|
return true if (n + m) == x
end
end
false
end
puts sum_comparison([1, 2, 3, 4], 5)
Output:
true
Problem
Your method is equivalent to:
def sum_comparison(int_array, x)
return int_array[0].to_i + int_array[1].to_i == x
end
Therefore,
int_array = [1,2,4,16,32,7,5,7,8,22,28]
sum_comparison(int_array, 3) #=> true, just lucky!
sum_comparison(int_array, 6) #=> false, wrong!
Alternative
Here is a relatively efficient implemention, certainly far more efficient than using Enumerable#combination.
Code
def sum_comparison(int_array, x)
sorted = int_array.sort
smallest = sorted.first
sorted_stub = sorted.take_while { |e| e+smallest <= x }
p "sorted_stub = #{sorted_stub}"
return false if sorted_stub.size < 2
loop do
return false if sorted_stub.size < 2
v = sorted_stub.shift
found = sorted_stub.find { |e| v+e >= x }
return true if found && v+found == x
end
false
end
Examples
sum_comparison([7,16,4,12,-2,5,8], 3)
# "sorted_stub = [-2, 4, 5]"
#=> true
sum_comparison([7,16,4,12,-2,5,8], 7)
# "sorted_stub = [-2, 4, 5, 7, 8]"
#=> false
sum_comparison([7,16,4,22,18,12,2,41,5,8,17,31], 9)
# "sorted_stub = [2, 4, 5, 7]"
#=> true
Notes
The line p "sorted_stub = #{sorted_stub}" is included merely to display the array sorted_stub in the examples.
If e+smallest > x for any elements f and g in sorted for which g >= e and f < g, f+g >= e+smallest > x. Ergo, sorted_stub.last is the largest value in sorted that need be considered.
For a given value v, the line found = sorted_stub.find { |e| v+e >= x } stops the search for a second value e for which v+e = x as soon as it finds e such that v+e >= x. The next line then determines if a match has been found.

Ruby multidimensional array

Maybe it's just my lack of abilities to find stuff here that is the problem, but I can't find anything about how to create multidimensional arrays in Ruby.
Could someone please give me an example on how to do it?
Strictly speaking it is not possible to create multi dimensional arrays in Ruby. But it is possible to put an array in another array, which is almost the same as a multi dimensional array.
This is how you could create a 2D array in Ruby:
a = [[1,2,3], [4,5,6], [7,8,9]]
As stated in the comments, you could also use NArray which is a Ruby numerical array library:
require 'narray'
b = NArray[ [1,2,3], [4,5,6], [7,8,9] ]
Use a[i][j] to access the elements of the array. Basically a[i] returns the 'sub array' stored on position i of a and thus a[i][j] returns element number j from the array that is stored on position i.
you can pass a block to Array.new
Array.new(n) {Array.new(n,default_value)}
the value that returns the block will be the value of each index of the first array,
so..
Array.new(2) {Array.new(2,5)} #=> [[5,5],[5,5]]
and you can access this array using array[x][y]
also for second Array instantiation, you can pass a block as default value too. so
Array.new(2) { Array.new(3) { |index| index ** 2} } #=> [[0, 1, 4], [0, 1, 4]]
Just a clarification:
arr = Array.new(2) {Array.new(2,5)} #=> [[5,5],[5,5]]
is not at all the same as:
arr = Array.new(2, Array.new(2, 5))
in the later case, try:
arr[0][0] = 99
and this is what you got:
[[99,5], [99,5]]
There are two ways to initialize multi array (size of 2).
All the another answers show examples with a default value.
Declare each of sub-array (you can do it in a runtime):
multi = []
multi[0] = []
multi[1] = []
or declare size of a parent array when initializing:
multi = Array.new(2) { Array.new }
Usage example:
multi[0][0] = 'a'
multi[0][1] = 'b'
multi[1][0] = 'c'
multi[1][1] = 'd'
p multi # [["a", "b"], ["c", "d"]]
p multi[1][0] # "c"
So you can wrap the first way and use it like this:
#multi = []
def multi(x, y, value)
#multi[x] ||= []
#multi[x][y] = value
end
multi(0, 0, 'a')
multi(0, 1, 'b')
multi(1, 0, 'c')
multi(1, 1, 'd')
p #multi # [["a", "b"], ["c", "d"]]
p #multi[1][0] # "c"
The method given above don't works.
n = 10
arr = Array.new(n, Array.new(n, Array.new(n,0.0)))
arr[0][1][2] += 1
puts arr[0][2][2]
is equivalent to
n = 10
a = Array.new(n,0.0)
b = Array.new(n,a)
arr = Array.new(n, b)
arr[0][1][2] += 1
puts arr[0][2][2]
and will print 1.0, not 0.0, because we are modifiyng array a and printing the element of array a.
Actually this is much quicker than the block method given above:
arr = Array.new(n, Array.new(n, Array.new(n,0.0)))
arr[0][1][2] += 1
I had to reproduce PHP-style multidimensional array in Ruby recently. Here is what I did:
# Produce PHP-style multidimensional array.
#
# Example
#
# arr = Marray.new
#
# arr[1][2][3] = "foo"
# => "foo"
#
# arr[1][2][3]
# => "foo"
class Marray < Array
def [](i)
super.nil? ? self[i] = Marray.new : super
end
end
Perhaps you can simulate your multidimensional Array with a Hash. The Hash-key can by any Ruby object, so you could also take an array.
Example:
marray = {}
p marray[[1,2]] #-> nil
marray[[1,2]] = :a
p marray[[1,2]] #-> :a
Based on this idea you could define a new class.
Just a quick scenario:
=begin rdoc
Define a multidimensional array.
The keys must be Fixnum.
The following features from Array are not supported:
* negative keys (Like Array[-1])
* No methods <<, each, ...
=end
class MArray
INFINITY = Float::INFINITY
=begin rdoc
=end
def initialize(dimensions=2, *limits)
#dimensions = dimensions
raise ArgumentError if limits.size > dimensions
#limits = []
0.upto(#dimensions-1){|i|
#limits << (limits[i] || INFINITY)
}
#content = {}
end
attr_reader :dimensions
attr_reader :limits
=begin rdoc
=end
def checkkeys(keys)
raise ArgumentError, "Additional key values for %i-dimensional Array" % #dimensions if keys.size > #dimensions
raise ArgumentError, "Missing key values for %i-dimensional Array" % #dimensions if keys.size != #dimensions
raise ArgumentError, "No keys given" if keys.size == 0
keys.each_with_index{|key,i|
raise ArgumentError, "Exceeded limit for %i dimension" % (i+1) if key > #limits[i]
raise ArgumentError, "Only positive numbers allowed" if key < 1
}
end
def[]=(*keys)
data = keys.pop
checkkeys(keys)
#content[keys] = data
end
def[](*keys)
checkkeys(keys)
#content[keys]
end
end
This can be used as:
arr = MArray.new()
arr[1,1] = 3
arr[2,2] = 3
If you need a predefined matrix 2x2 you can use it as:
arr = MArray.new(2,2,2)
arr[1,1] = 3
arr[2,2] = 3
#~ arr[3,2] = 3 #Exceeded limit for 1 dimension (ArgumentError)
I could imagine how to handle commands like << or each in a two-dimensional array, but not in multidimensional ones.
It might help to remember that the array is an object in ruby, and objects are not (by default) created simply by naming them or naming a the object reference. Here is a routine for creating a 3 dimension array and dumping it to the screen for verification:
def Create3DimensionArray(x, y, z, default)
n = 0 # verification code only
ar = Array.new(x)
for i in 0...x
ar[i] = Array.new(y)
for j in 0...y
ar[i][j] = Array.new(z, default)
for k in 0...z # verification code only
ar[i][j][k] = n # verification code only
n += 1 # verification code only
end # verification code only
end
end
return ar
end
# Create sample and verify
ar = Create3DimensionArray(3, 7, 10, 0)
for x in ar
puts "||"
for y in x
puts "|"
for z in y
printf "%d ", z
end
end
end
Here is an implementation of a 3D array class in ruby, in this case the default value is 0
class Array3
def initialize
#store = [[[]]]
end
def [](a,b,c)
if #store[a]==nil ||
#store[a][b]==nil ||
#store[a][b][c]==nil
return 0
else
return #store[a][b][c]
end
end
def []=(a,b,c,x)
#store[a] = [[]] if #store[a]==nil
#store[a][b] = [] if #store[a][b]==nil
#store[a][b][c] = x
end
end
array = Array3.new
array[1,2,3] = 4
puts array[1,2,3] # => 4
puts array[1,1,1] # => 0

Resources