getting dimension of multidimensional array in ruby - ruby

I just started learning ruby.
Now I need to figure out the dimension of a multidimensional array. I had a look at ruby-docs for the all the array methods, but I could not find a method that returns the dimension.
Here is an example:
For [[1, 2],[3,4],[5,6]] the dimension should be 2.
For [[[1,2],[2,3]],[[3,4],[5]]], the dimension should be 3.

Simple, object-oriented solution.
class Array
def depth
map {|element| element.depth + 1 }.max
end
end
class Object
def depth
0
end
end

There is not a built-in function for that as there may be multiple definition as to what you mean by "dimension" for an array. Ruby's arrays may contain anything including hashes or other arrays. That's why I believe you need to implement your own function for that.
Asuming that by dimension you mean "the deepest nested level of arrays" this should do the trick:
def get_dimension a
return 0 if a.class != Array
result = 1
a.each do |sub_a|
if sub_a.class == Array
dim = get_dimension(sub_a)
result = dim + 1 if dim + 1 > result
end
end
return result
end
EDIT: and as ruby is a great language and allows you to do some fancy stuff you can also make get_dimension a method of Array:
class Array
def get_dimension
... # code from above slightly modified
end
end

in the simplest case
depth = Proc.new do |array|
depth = 1
while Array === array.first do
array = array.first
depth += 1
end
depth
end
array = [[[1,2],[2,3]],[[3,4],[5]]]
depth.call(array)
#=> 3
Or this tiny recursive method
def depth(array, depth=1)
array = array.send(:first)
Array === array ? depth(array, depth+1) : depth
end
array = [[[1,2],[2,3]],[[3,4],[5]]]
depth(array)
#=> 3

How about:
class Object
def dimension
self.class == Array ? 1 + self[0].dimension : 0
end
end
[[[1,2],[2,3]],[[3,4],[5]]].dimension
#=> 3

As a modification of Tass's approach:
class Array
def depth
map{ |element| element.is_a?( Vector ) ? element.depth + 1 : 1 }.max
end
end
Keeps depth as a method of Array, and doesn't require adding a method to Object.
Of course, that might be what you want if you are going to call my_object.depth, where you don't know in advance that my_object.class == Array

I was not satisfied with the other solutions so I wrote a one-liner I'd actually use:
def depth(array)
array.to_a == array.flatten(1) ? 1 : depth(array.flatten(1)) + 1
end
It will flatten the array 1 dimension at the time until it can't flatten anymore, while counting the dimensions.
Why is this better?
doesn't require modification to native classes (avoid that if possible)
doesn't use metaprogramming (is_a?, send, respond_to?, etc.)
fairly easy to read
works with hashes as well (notice array.to_a)
actually works (unlike only checking the first branch, and other silly stuff)

Related

Using range.each vs while-loop to work with sequence of numbers in Ruby

Total beginner here, so I apologize if a) this question isn't appropriate or b) I haven't asked it properly.
I'm working on simple practice problems in Ruby and I noticed that while I arrived at a solution that works, when my solution runs in a visualizer, it gives premature returns for the array. Is this problematic? I'm also wondering if there's any reason (stylistically, conceptually, etc.) why you would want to use a while-loop vs. a for-loop with range for a problem like this or fizzbuzz.
Thank you for any help/advice!
The practice problem is:
# Write a method which collects all numbers between small_num and big_num into
an array. Ex: range(2, 5) => [2, 3, 4, 5]
My solution:
def range(small_num, big_num)
arr = []
(small_num..big_num).each do |num|
arr.push(num)
end
return arr
end
The provided solution:
def range(small_num, big_num)
collection = []
i = small_num
while i <= big_num
collection << i
i += 1
end
collection
end
Here's a simplified version of your code:
def range(small_num, big_num)
arr = [ ]
(small_num..big_num).each do |num|
arr << num
end
arr
end
Where the << or push function does technically have a return value, and that return value is the modified array. This is just how Ruby works. Every method must return something even if that something is "nothing" in the form of nil. As with everything in Ruby even nil is an object.
You're not obligated to use the return values, though if you did want to you could. Here's a version with inject:
def range(small_num, big_num)
(small_num..big_num).inject([ ]) do |arr, num|
arr << num
end
end
Where the inject method takes the return value of each block and feeds it in as the "seed" for the next round. As << returns the array this makes it very convenient to chain.
The most minimal version is, of course:
def range(small_num, big_num)
(small_num..big_num).to_a
end
Or as Sagar points out, using the splat operator:
def range(small_num, big_num)
[*small_num..big_num]
end
Where when you splat something you're in effect flattening those values into the array instead of storing them in a sub-array.

Ruby array initialize on read

I would like to be able to do this:
my_array = Array.new
my_array[12] += 1
In other words, somehow upon trying to access entry 12, finding it uninitialized, it is initialized to zero so I can add one to it. Array.new has a default: parameter, but that comes into play when you initialize the array with a known number of slots. Other than writing my own class, is there a ruby-ish way of doing this?
No need to create a new class :
my_hash = Hash.new(0)
my_hash[12] += 1
p my_hash
#=> {12=>1}
For many cases, hashes and arrays can be used interchangeably.
An array with an arbitrary number of elements and a default value sounds like a hash to me ;)
Just to make it clear : Hash and Array aren't equivalent. There will be cases where using a hash instead of an array will be completely wrong.
Something like:
a[12] = (a[12] ||= 0) + 1
Making use of nil.to_i == 0
my_array = Array.new
my_array[12] = my_array[12].to_i + 1
Note, that unlike other solutions here so far, this one works for any arbitrary initial value.
my_array = Array.new.extend(Module.new {
def [] idx
super || 0
end
})
my_array[12] += 1
#⇒ 1
This is not possible with the stock Array::new method.
https://docs.ruby-lang.org/en/2.0.0/Array.html#method-c-new
You will either need to monkey patch Array class, or monkey patch nil class. And they are not recommended.
If you have a specific use case, I would create a new wrapper class around Array
class MyArray < Array
def [](i)
super(i) ? super(i) : self[i] = 0
end
end
arr = MyArray.new
arr[12] += 1 # => 1

Is there a better way?: iterating over an array in ruby

I'm working on a mini project for a summer class. I'd like some feedback on the code I have written, especially part 3.
Here's the question:
Create an array called numbers containing the integers 1 - 10 and assign it to a variable.
Create an empty array called even_numbers.
Create a method that iterates over the array. Place all even numbers in the array even_numbers.
Print the array even_numbers.
Here's my code, so far:
numbers = [1,2,3,4,5,6,7,8,9,10]
print numbers[3]
even_numbers.empty?
def even_numbers
numbers.sort!
end
Rather than doing explicit iteration, the best way is likely Array#select thus:
even_numbers = numbers.select { |n| n.even? }
which will run the block given on each element in the array numbers and produce an array containing all elements for which the block returned true.
or an alternative solution following the convention of your problem:
def get_even_numbers(array)
even_num = []
array.each do |n|
even_num << n if n.even?
end
even_num
end
and of course going for the select method is always preferred.

Subtraction of two arrays with incremental indexes of the other array to a maximum limit

I have lots of math to do on lots of data but it's all based on a few base templates. So instead of say, when doing math between 2 arrays I do this:
results = [a[0]-b[1],a[1]-b[2],a[2]-b[3]]
I want to instead just put the base template: a[0]-b[1] and make it automatically fill say 50 places in the results array. So I don't always have to manually type it.
What would be the ways to do that? And would a good way be to create 1 method that does this automatically. And I just tell it the math and it fills out an array?
I have no clue, I'm really new to programming.
a = [2,3,4]
b = [1,2,3,4]
results = a.zip(b.drop(1)).take(50).map { |v,w| v - w }
Custom
a = [2,3,4..............,1000]
b = [1,2,3,4,.............900]
class Array
def self.calculate_difference(arr1,arr2,limit)
begin
result ||= Array.new
limit.send(:times) {|index| result << arr1[index]-arr2[index+=1]}
result
rescue
raise "Index/Limit Error"
end
end
end
Call by:
Array.calculate_difference(a,b,50)

please help with my "shuffle" code in ruby

this is the question
Shuffle. Now that you’ve finished your
new sorting algorithm, how about the
opposite? Write a shuffle method that
takes an array and returns a totally
shuffled version. As always, you’ll
want to test it, but testing this one
is trickier: How can you test to make
sure you are getting a perfect
shuffle? What would you even say a
perfect shuffle would be? Now test for
it.
This is my code answer:
def shuffle arr
x = arr.length
while x != 0
new_arr = []
rand_arr = (rand(x))
x--
new_arr.push rand_arr
arr.pop rand_arr
end
new_arr
end
puts (shuffle ([1,2,3]))
What are my mistakes? Why doesn't this code work?
Here's a far more Rubyish version:
class Array
def shuffle!
size.downto(1) { |n| push delete_at(rand(n)) }
self
end
end
puts [1,2,3].shuffle!
Here's a more concise way of writing it:
def shuffle(arr)
new_arr = []
while (arr.any?) do
new_arr << arr.delete_at(rand(arr.length))
end
new_arr
end
And some tests:
5.times do
puts shuffle((1..5).to_a).join(',')
end
>> 4,2,1,3,5
>> 3,2,1,4,5
>> 4,2,5,1,3
>> 5,2,1,4,3
>> 4,3,1,5,2
Beside minor other errors you seems not to understand what pop and push are doing (taking or adding some items from the end of the array).
You are probably trying to write something like below.
def shuffle arr
x = arr.length
new_arr = []
while x != 0
randpos = rand(x)
x = x-1
item = arr[randpos]
new_arr.push item
arr[randpos] = arr[x]
arr.pop
end
new_arr
end
puts (shuffle ([1,2,3]))
You're getting your indexes mixed up with your values. When you do new_arr.push rand_arr, you're putting whatever random index you came up with as a value on the end of new_arr. What you meant to do is new_arr.push arr[rand_arr], where arr[rand_arr] is the value at the index rand_arr in arr.
Ruby 1.8.7 and 1.9.2 have a built-in Array#shuffle method.
A variant of Mark Thomas's answer. His algorithm can be quite slow with a large array, due to delete operation performance.
class Array
def shuffle!
size.downto(1) do |n|
index=rand(n)
# swap elements at index and the end
self[index], self[size-1] = self[size-1],self[index]
end
self
end
end
puts [1,2,3].shuffle!
This algorithm is O(size), while Mark's algorithm is O(size^2). On my computer, Mark's answer takes 400 seconds to shuffle an array of 1,000,000 elements on my machine, versus 0.5 seconds with my method.

Resources