Ruby multidimensional array - ruby

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

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.

Find all indices of a substring within a string

I want to be able to find the index of all occurrences of a substring in a larger string using Ruby. E.g.: all "in" in "Einstein"
str = "Einstein"
str.index("in") #returns only 1
str.scan("in") #returns ["in","in"]
#desired output would be [1, 6]
The standard hack is:
indices = "Einstein".enum_for(:scan, /(?=in)/).map do
Regexp.last_match.offset(0).first
end
#=> [1, 6]
def indices_of_matches(str, target)
sz = target.size
(0..str.size-sz).select { |i| str[i,sz] == target }
end
indices_of_matches('Einstein', 'in')
#=> [1, 6]
indices_of_matches('nnnn', 'nn')
#=> [0, 1, 2]
The second example reflects an assumption I made about the treatment of overlapping strings. If overlapping strings are not to be considered (i.e., [0, 2] is the desired return value in the second example), this answer is obviously inappropriate.
This is a more verbose solution which brings the advantage of not relying on a global value:
def indices(string, regex)
position = 0
Enumerator.new do |yielder|
while match = regex.match(string, position)
yielder << match.begin(0)
position = match.end(0)
end
end
end
p indices("Einstein", /in/).to_a
# [1, 6]
It outputs an Enumerator, so you could also use it lazily or just take the n first indices.
Also, if you might need more information than just the indices, you could return an Enumerator of MatchData and extract the indices:
def matches(string, regex)
position = 0
Enumerator.new do |yielder|
while match = regex.match(string, position)
yielder << match
position = match.end(0)
end
end
end
p matches("Einstein", /in/).map{ |match| match.begin(0) }
# [1, 6]
To get the behaviour described by #Cary, you could replace the last line in block by position = match.begin(0) + 1.
#Recursive Function
def indexes string, sub_string, start=0
index = string[start..-1].index(sub_string)
return [] unless index
[index+start] + indexes(string,sub_string,index+start+1)
end
#For better Usage I would open String class
class String
def indexes sub_string,start=0
index = self[start..-1].index(sub_string)
return [] unless index
[index+start] + indexes(sub_string,index+start+1)
end
end
This way we can call in this way: "Einstein".indexes("in") #=> [1, 6]

for each loop with increase array

I am trying to iterate through an array using a for each loop in ruby but inside of the loop I am increasing the size of the array conditionally. I want to iterate through the array until I have run the iterate with every element in the array including all the ones I have added
for x in fol
t = get_transition(x,"")
for i in t
if i != nil && !fol.include?(i)
fol = fol.push(i)
fol = fol.flatten
end
end
end
In the first loop of this code the array
fol = [1]
and it adds the element 3 to the array creating
fol = [1, 3]
It will then run the loop again with x = 3 and the array becomes
fol = [1, 3, 2]
But it will not iterate again with x = 2.
Thank you in advance for any assistance
For clarification purposes I have added in print statements and the output that they generate.
fol.each do |x|
puts "fol = #{fol}"
puts "x = #{x}"
t = get_transition(x,"")
puts "t = #{t}"
t.each do |i|
puts "i = #{i}"
if i != nil && !fol.include?(i)
fol = fol.push(i)
fol = fol.flatten
end
end
end
puts "\nfol = #{fol}"
This code generates this output
fol = [1]
x = 1
t = [3]
i = 3
fol = [1, 3]
x = 3
t = [2]
i = 2
fol = [1, 3, 2]
I am trying to iterate through an array using a for each loop in ruby
but inside of the loop I am increasing the size of the array
conditionally. I want to iterate through the array until I have run
the iterate with every element in the array including all the ones I
have added
Why not just treat this as a queue? That's pretty much what you've described
queue = fol.clone
until queue.empty?
x = queue.pop
t = get_transition(x,"")
for i in t
if i != nil && !fol.include?(i)
# Not too sure what type "i" is here, but you push onto the queue here
# I'd try to avoid flattening if you know what your data types are as it will be slow
end
end
end

How do I break out of a map/collect and return whatever has been collected up to that point?

I'm rewriting this question in code:
many = 1000
# An expensive method.
#
# It returns some data or nil if no result is available.
expensive_method = lambda do
rand(5) == 0 ? nil : "foo"
end
# Now, let's collect some data and stop collecting when no more data is
# available.
# This is concise but doesn't work.
collection = many.times.map do
expensive_method.call || break
end
puts collection.is_a? Array # false
# This is less concise but works.
collection = []
many.times do
collection << (expensive_method.call || break)
end
puts collection.is_a? Array # true
# My inner Rubyist ponders: Is it possible to accomplish this more concisely
# using map?
Sure seems the only way to do this in Ruby is a filter type method then passing results to map. I'm not sure if this works in 1.8, but in 1.9 you could:
[0,1,2,1,0].take_while {|val| val < 2}.map(&:some_function)
Or in the times example
3.times.take_while {|count| count <= 1 } #=> [0,1]
Instead of using map directly, build up your own collection and then use the fact that break returns a value to abort early:
result =
[0, 1, 2, 1, 0].each_with_object([]) { |val, accumulator|
if val < 2
accumulator << val
else
break accumulator
end
}
result # => [0, 1]
If we did just break (instead of break accumulator) then nil would be implicitly returned and result would just be set to nil.
This solution has the advantage of only allocating a single accumulator Array and only having to loop once.
If you really mean "up to the break", [0,1,2,1,0] should result in [0,1], not [0,1,1,0]. The only way in Ruby that I know about is break in a loop. Functional approach could be much slower as you don't actually break:
r =
[0,1,2,1,0].inject([true, []]) do |(f, a), i|
if f
if i > 1
[false, a]
else
[f, a << i]
end
else
[f, a]
end
end
puts r.last.inspect
Compare with:
r = []
[0,1,2,1,0].each do |i|
break if i > 1
r << i
end
puts r.inspect
Tail recursion is out of the question for Ruby, this is how things are done in true functional languages.
Breaking map doesn't work for me, result is nil.
Added: As #dogenpunk pointed out, there is take_while (and drop_while in fact), which is probably a better alternative, only it always creates temporary array which may or may not be the a problem.
irb(main):011:0> 3.times.select {|count| count <= 1}
=> [0, 1]
or
irb(main):014:0> 3.times.reject {|count| count > 1}
=> [0, 1]
How about:
odd_index = my_collection.index{|item| odd_condition(item)}
result = odd_index == 0 ? [] : my_collection[0..odd_index.to_i - 1]
3.times.map do |count|
count > 1 ? nil : rand
end.compact

How do I find .index of a multidimensional array

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

Resources