Ruby: how to know depth of multidemensional array - ruby

This is my problem I have met in my assignment.
Array A has two elements: array B and array C.
Array B has two elements: array D and array E
At some point, array X just contains two elements: string a and string b.
I don't know how to determine how deep array A is. For example:
arrA = [
[
[1,2]
]
]
I have tested by: A[0][0][0] == nil which returns false. Moreover, A[0][0]..[0] == nil always returns false. So, I cannot do this way to know how deep array A is.

If this is not what you're looking for, it should be a good starting point:
def depth (a)
return 0 unless a.is_a?(Array)
return 1 + depth(a[0])
end
> depth(arrA)
=> 3
Please note that this only measures the depth of the first branch.

My solution which goes below answers the maximum depth of any array:
Example: for arr=[ [[1],[2,3]], [[[ 3,4 ]]] ], the maximum depth of arr is 4 for 3,4.
Aprroach - flatten by one level and compare
b, depth = arr.dup, 1
until b==arr.flatten
depth+=1
b=b.flatten(1)
end
puts "Array depth: #{depth}" #=> 4
Hope it answers your question.

A simple pure functional recursive solution:
def depth(xs, n=0)
return case
when xs.class != Array
n
when xs == []
n + 1
else
xs.collect{|x| depth x, n+1}.max
end
end
Examples:
depth([]) == 1
depth([['a']])) == 2
depth([1, 2, 3, 4, [1, 2, 3, [[2, 2],[]], 4, 5, 6, 7], 5, 5, [[[[[3, 4]]]]], [[[[[[[[[1, 2]]]]]]]]]]) == 10

Here's a one-liner similar to kiddorails' solution extracted into a method:
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 than other solutions out there?
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 on hashes as well (notice array.to_a)
actually works (unlike only checking the first branch, and other silly stuff)

Also one line code if you want to use
def depth (a)
a.to_s.count("[")
end

Related

Find the odd int - Ruby Nested Loop Error

I was doing this question on codewars: "Given an array, find the int that appears an odd number of times. There will always be only one integer that appears an odd number of times."
Code:
def find_it(seq)
int = []
for a in seq do
count = 0
for b in seq do
if a == b
count += 1
end
end
if count % 2.0 != 0
int << b
end
end
puts int.uniq[0].to_i
end
It was tested against a couple inputs, but the answers were wrong for these two arrays:
find_it([1,1,2,-2,5,2,4,4,-1,-2,5]) - returns 5 instead of -1
find_it([1,1,1,1,1,1,10,1,1,1,1]) - returns 1 instead of 10
What went wrong with my code?
if count % 2.0 != 0
int << b
end
The problem you have here is that your pushing b instead of a into the integer array, so what's happening is that instead of the value that you counted being pushed in, your pushing in the last value of b which is the last value element in the array regardless as long as the condition that the counter is an odd number, although b and counter have nothing to do with each other. so to fix it you replace b with a so that it pushes in the value you are testing comparing with the other elements in the second loop
fix:
if count % 2.0 != 0
int << a
end
a similar yet simpler code that does a similar job except in a shorter and more understandable way is:
def find_it(seq)
numberWithOddCount = 0
seq.each do |currentElement|
counter = 0
seq.each { |elementToCompare| counter += 1 if currentElement == elementToCompare}
numberWithOddCount = currentElement if counter % 2 != 0
end
numberWithOddCount
end
Just added a few tid-bits that you could also utilize to shorten and simplify code.
Happy Coding!
Note:
You could utilize built in ruby methods in creative ways to make the code do what you want in very few lines (or even one line) such as what #iGian did in the questions comments, but if your still new to ruby then its best to utilize those methods one by one when learning them otherwise you'll be confused. But if your willing to take the time now to learn them, I suggest you take his code and separate each method execution into its own line and output what each method had done to know what's doing what. and practice using each separately.
#aimen_alt is right about your mistake
but let's decompose your problem.
First, you need to calculate the appearances of each number.
Second, you need to find the one with the odd count of the appearances.
Accordingly to the problem, there is only one such number, so you can return it right away.
You can go your way and do it in O(N^2) complexity by scanning your sequence for each item in the sequence (so N items in the sequence multiply by the size of the sequence N = N*N). You can do it linearly* by constructing a Hash and than you'll be able to get the key with odd value:
def find_it(seq)
numbers = {}
seq.each do |item|
numbers[item] = numbers[item].to_i + 1
end
numbers.select{ |k,v| v.odd? }.first.first
end
to be more idiomatic you can use group_by to group the numbers themselves:
seq = [1, 2, 6, 1, 2]
seq.group_by{ |item| item }
#=> {1=>[1, 1], 2=>[2, 2], 6=>[6]}
You can see that each value is an Array, and you just need to get one with the odd amount of items:
seq = [1, 2, 6, 1, 2]
seq.group_by{ |item| item }.select{ |k, v| v.size.odd? }
#=> {6=>[6]}
And the last thing you would like to do is to get the value of the key:
seq.group_by{ |item| item }.select{ |k, v| v.size.odd? }.keys.first
So, the final solution would be
def find_it(seq)
seq.group_by{ |item| item }
.select{ |k, v| v.size.odd? }
.keys
.first
end
as #pascalbetz mentioned:
def find_it(seq)
seq.group_by{ |item| item }
.find{ |k, v| v.size.odd? }
.first
end
def find_it(seq)
seq.group_by{|x| x}.select{|k, v| (v.count % 2.0 !=0)}.first[0]
end
The above code will take a sequence in an array. Here we are grouping by elements:
For example:
[1,1,2,-2,5,2,4,4,-1,-2,5].group_by{|x| x}
# => {1=>[1, 1], 2=>[2, 2], -2=>[-2, -2], 5=>[5, 5], 4=>[4, 4], -1=>[-1]}
after getting the above results, we are finding the whose elements count not odd with the select condition.
ex:
[1,1,2,-2,5,2,4,4,-1,-2,5].group_by{|x| x}.select{|k, v| (v.count % 2.0 !=0)}
we will get the results as {-1=>[-1]}
we are taking the key as result element.
What about this one
def find_it(seq)
seq.reduce(:^)
end
^ -> this symbol is bitwise XOR.
reduce function is taking each value and doing whatever work assigned inside. In this case, it's taking each element and doing an XOR operation. the first element is doing XOR with zero and the next element doing XOR with the previous result and so on.
In this way, we found the odd element.
How XOR operation work
0 ^ 2 = 2
4 ^ 4 = 0
If you want to know more about XOR. kindly refer to this.
Thank you for all the detailed answers, I'm going over everyone's answers now. I'm new to Ruby, and I'm still in the process of learning the methods/rules of using them/Big O notation, so I much appreciated everyone's input. Codewar listed some top ranked solutions. This seems to be the fastest so far:
def find_it(seq)
seq.detect { |n| seq.count(n).odd? }
end

Testing array elements vs multiple criteria

I am trying to create a method complex_check(n), which will do the following:
Create an array of integers ascending from 0 to n
Check each element of that array against complex criteria.
For example, I have an array from 0..n, and I want to know which elements of the array are both evenly divisible by 3 and not divisible by 4. I can index through to check for single criteria like odd?, etc., but is there a compact way to check each integer against multiple criteria?
This is the best I could come up with, I think this is sort of what you're trying to do:
def complex_check(n)
check_array = (0..n).to_a
check_array.select { |num|
num % 3 == 0 &&
num % 4 != 0
}
end
Then using it:
complex_check(15)
=> [3, 6, 9, 15]
One of the things Ruby is very good at is processing through lists and other regular data structures. The Enumerable extensions common to many things including Array allow you to quickly filter, split, chunk, and otherwise completely rework the data you're dealing with. Often a few quick alterations can do the job.
In this case select can be used to filter out undesirable values from your potential candidates in the range 0 to n:
def complex_check(n)
(0..n).select do |v|
v % 3 == 0 and v % 4 != 0
end
end
The key here is using select where any block that returns a logically true value will be a signal to include the element, and otherwise to exclude it. The opposite of this is reject which works on the same principle, just with inverted logic.
You could also pass these filters in dynamically:
def complex_check(n, *tests)
tests.each_with_object((0..n).to_a) do |test, a|
a.select!(&test)
end
end
Where your test code ends up looking like this:
complex_check(
50,
-> (v) { v % 3 == 0 },
-> (v) { v % 4 != 0 }
)
Then you can plug in tests using lambdas which are little reusable blocks.
create an array of integers ascending from 0 to n
Don't do this if you don't have to, which is almost never.
Use a Range or an Enumerator. They are real easy:
0.upto(n).select do |i| #0.upto(n) results in a Enumerator
#complex criteria
end
#or
def complex_check(n)
0.step(n,3).reject{|n| (n%4).zero?} #0.step(n,3) is an Enumerator
end
p complex_check(15) #= [3, 6, 9, 15]

Check if the sum of two different numbers in an array equal a variable number?

In Ruby, I would like to take an array of numbers, select 2 different numbers, add those 2 numbers together and see weather there equal to a variable x.y'd a variable x. Here is the code I used
def arrayIsEqual? (numArray, x)
return true if numArray.sample + numArray.sample == x
return false if numArray.empty? || numArray.count == 1
end
for example
numArray = [4,2,7,5]
x = 11
arrayIsEqual (numArray, n) should return true, since 4 + 7 = n(11)
How do I get this to work?
I don't want it to be 2 random numbers, just any 2 different numbers that add up to n
It looks like you're trying to see if there are any two numbers in the array that add up to the specified value x. However, your code just picks two numbers at random and checks if those numbers add up.
Ruby has the Array#combination method, which generates all combinations of a given length:
def contains_pair_for_sum?(arr, n)
!!arr.uniq.combination(2).detect { |a, b| a + b == n }
end
A few things to note:
First, we named it according to Ruby conventions: each word is separated_by_underscores. The ? on the end means that the method is a predicate method and returns a true or false value.
Inside the method, a few things happen. Let's look at that line, piece by piece.
arr: We take the array that was passed in.
<...>.uniq: We only look at the unique elements (because the OP wants to pick two different numbers).
<...>.combination(2): We ask for all combinations from the array of length 2. If the array was [4, 5, 6], we'd get [[4, 5], [4, 6], [5, 6]].
<...>.detect { |a, b| a + b == n }: We look for the first combination that adds up to n. If we found one, that's the result of that method. Otherwise, we get nil.
!!<...>: Finally, we take the result we got from detect and negate it twice. The first negation produces a Boolean value (true if the value we got was nil, or false if it's anything else); the second negation produces a Boolean value that's identical to the truth value of the first negation. This is a Ruby idiom to coerce a result into being either true or false.
Let's see it in action:
array = [4, 5, 9, 7, 8]
contains_pair_for_sum?(array, 11)
# => true (because [4, 7] sums to 11)
contains_pair_for_sum?(array, 17)
# => true (because [9, 8] sums to 17)
contains_pair_for_sum?(array, 100)
# => false (no pair matched)
I understand that your question is "is there any pair of numbers in my array equals x", in which case this will do what you need:
def has_pair_equal?(num_array, x)
(0..num_array.length-1).any? do |i|
num_array[i+1..-1].any? { |n| n + num_array[i] == x }
end
end
This checks all sums of pairs of numbers in the array, and checks if their sum is x. sample randomly picks an item from the array, which means that what your code does is "return true sometimes if there is a pair of numbers in my array equals x"
def array_is_equal? (num_array, x)
equality = 0
num_array.each do |a|
equality += 1 if a == x
return true if equality == 2
end
return false
end
Use lowercase and underscores for variables in Ruby. The convention is different here than in some other languages.
One liner
x=[4,2,7,5]; x.each_with_index.any? {|y,i| x.each_with_index.any? {|z,j| unless i==j; z+y==11; end } }
And as a function
def pair_sum_match?(arr, x)
arr.each_with_index.any? do |y,i|
arr.each_with_index.any? do |z,j|
unless i==j
z+y==x
end
end
end
end
Updated: Added each_with_index to avoid self inclusion on checks. It's a lot longer now :-/
Just iterate over it once and use the target number to see if it matches. 100 times faster then most of the answers here
numbers = ( -10..10 ).to_a
numbers.unshift( numbers.first + -1 ) # if you do -20 or 20
numbers.push( numbers.last + 1 )
target = 5
searched = { }
matches = { }
numbers.each do |number|
if searched[ target - number + 1 ] == true
matches[ "#{ number }_plus_#{ target - number }" ] = target
end
searched[ number + 1 ] = true
end
ap matches

Ruby - Find element not in common for two arrays

I've been thinking about a following problem - there are two arrays, and I need to find elements not common for them both, for example:
a = [1,2,3,4]
b = [1,2,4]
And the expected answer is [3].
So far I've been doing it like this:
a.select { |elem| !b.include?(elem) }
But it gives me O(N ** 2) time complexity. I'm sure it can be done faster ;)
Also, I've been thinking about getting it somehow like this (using some method opposite to & which gives common elements of 2 arrays):
a !& b #=> doesn't work of course
Another way might be to add two arrays and find the unique element with some method similar to uniq, so that:
[1,1,2,2,3,4,4].some_method #=> would return 3
The simplest (in terms of using only the arrays already in place and stock array methods, anyway) solution is the union of the differences:
a = [1,2,3,4]
b = [1,2,4]
(a-b) | (b-a)
=> [3]
This may or may not be better than O(n**2). There are other options which are likely to give better peformance (see other answers/comments).
Edit: Here's a quick-ish implementation of the sort-and-iterate approach (this assumes no array has repeated elements; otherwise it will need to be modified depending on what behavior is wanted in that case). If anyone can come up with a shorter way to do it, I'd be interested. The limiting factor is the sort used. I assume Ruby uses some sort of Quicksort, so complexity averages O(n log n) with possible worst-case of O(n**2); if the arrays are already sorted, then of course the two calls to sort can be removed and it will run in O(n).
def diff a, b
a = a.sort
b = b.sort
result = []
bi = 0
ai = 0
while (ai < a.size && bi < b.size)
if a[ai] == b[bi]
ai += 1
bi += 1
elsif a[ai]<b[bi]
result << a[ai]
ai += 1
else
result << b[bi]
bi += 1
end
end
result += a[ai, a.size-ai] if ai<a.size
result += b[bi, b.size-bi] if bi<b.size
result
end
As #iamnotmaynard noted in the comments, this is traditionally a set operation (called the symmetric difference). Ruby's Set class includes this operation, so the most idiomatic way to express it would be with a Set:
Set.new(a) ^ b
That should give O(n) performance (since a set membership test is constant-time).
a = [1, 2, 3]
b = [2, 3, 4]
a + b - (a & b)
# => [1, 4]
The solution for Array divergences is like:
a = [1, 2, 3]
b = [2, 3, 4]
(a - b) | (b - a)
# => [1, 4]
You can also read my blog post about Array coherences

Code to write a random array of x numbers with no duplicates [duplicate]

This is what I have so far:
myArray.map!{ rand(max) }
Obviously, however, sometimes the numbers in the list are not unique. How can I make sure my list only contains unique numbers without having to create a bigger list from which I then just pick the n unique numbers?
Edit:
I'd really like to see this done w/o loop - if at all possible.
(0..50).to_a.sort{ rand() - 0.5 }[0..x]
(0..50).to_a can be replaced with any array.
0 is "minvalue", 50 is "max value"
x is "how many values i want out"
of course, its impossible for x to be permitted to be greater than max-min :)
In expansion of how this works
(0..5).to_a ==> [0,1,2,3,4,5]
[0,1,2,3,4,5].sort{ -1 } ==> [0, 1, 2, 4, 3, 5] # constant
[0,1,2,3,4,5].sort{ 1 } ==> [5, 3, 0, 4, 2, 1] # constant
[0,1,2,3,4,5].sort{ rand() - 0.5 } ==> [1, 5, 0, 3, 4, 2 ] # random
[1, 5, 0, 3, 4, 2 ][ 0..2 ] ==> [1, 5, 0 ]
Footnotes:
It is worth mentioning that at the time this question was originally answered, September 2008, that Array#shuffle was either not available or not already known to me, hence the approximation in Array#sort
And there's a barrage of suggested edits to this as a result.
So:
.sort{ rand() - 0.5 }
Can be better, and shorter expressed on modern ruby implementations using
.shuffle
Additionally,
[0..x]
Can be more obviously written with Array#take as:
.take(x)
Thus, the easiest way to produce a sequence of random numbers on a modern ruby is:
(0..50).to_a.shuffle.take(x)
This uses Set:
require 'set'
def rand_n(n, max)
randoms = Set.new
loop do
randoms << rand(max)
return randoms.to_a if randoms.size >= n
end
end
Ruby 1.9 offers the Array#sample method which returns an element, or elements randomly selected from an Array. The results of #sample won't include the same Array element twice.
(1..999).to_a.sample 5 # => [389, 30, 326, 946, 746]
When compared to the to_a.sort_by approach, the sample method appears to be significantly faster. In a simple scenario I compared sort_by to sample, and got the following results.
require 'benchmark'
range = 0...1000000
how_many = 5
Benchmark.realtime do
range.to_a.sample(how_many)
end
=> 0.081083
Benchmark.realtime do
(range).sort_by{rand}[0...how_many]
end
=> 2.907445
Just to give you an idea about speed, I ran four versions of this:
Using Sets, like Ryan's suggestion.
Using an Array slightly larger than necessary, then doing uniq! at the end.
Using a Hash, like Kyle suggested.
Creating an Array of the required size, then sorting it randomly, like Kent's suggestion (but without the extraneous "- 0.5", which does nothing).
They're all fast at small scales, so I had them each create a list of 1,000,000 numbers. Here are the times, in seconds:
Sets: 628
Array + uniq: 629
Hash: 645
fixed Array + sort: 8
And no, that last one is not a typo. So if you care about speed, and it's OK for the numbers to be integers from 0 to whatever, then my exact code was:
a = (0...1000000).sort_by{rand}
Yes, it's possible to do this without a loop and without keeping track of which numbers have been chosen. It's called a Linear Feedback Shift Register: Create Random Number Sequence with No Repeats
[*1..99].sample(4) #=> [64, 99, 29, 49]
According to Array#sample docs,
The elements are chosen by using random and unique indices
If you need SecureRandom (which uses computer noise instead of pseudorandom numbers):
require 'securerandom'
[*1..99].sample(4, random: SecureRandom) #=> [2, 75, 95, 37]
How about a play on this? Unique random numbers without needing to use Set or Hash.
x = 0
(1..100).map{|iter| x += rand(100)}.shuffle
You could use a hash to track the random numbers you've used so far:
seen = {}
max = 100
(1..10).map { |n|
x = rand(max)
while (seen[x])
x = rand(max)
end
x
}
Rather than add the items to a list/array, add them to a Set.
If you have a finite list of possible random numbers (i.e. 1 to 100), then Kent's solution is good.
Otherwise there is no other good way to do it without looping. The problem is you MUST do a loop if you get a duplicate. My solution should be efficient and the looping should not be too much more than the size of your array (i.e. if you want 20 unique random numbers, it might take 25 iterations on average.) Though the number of iterations gets worse the more numbers you need and the smaller max is. Here is my above code modified to show how many iterations are needed for the given input:
require 'set'
def rand_n(n, max)
randoms = Set.new
i = 0
loop do
randoms << rand(max)
break if randoms.size > n
i += 1
end
puts "Took #{i} iterations for #{n} random numbers to a max of #{max}"
return randoms.to_a
end
I could write this code to LOOK more like Array.map if you want :)
Based on Kent Fredric's solution above, this is what I ended up using:
def n_unique_rand(number_to_generate, rand_upper_limit)
return (0..rand_upper_limit - 1).sort_by{rand}[0..number_to_generate - 1]
end
Thanks Kent.
No loops with this method
Array.new(size) { rand(max) }
require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
Array.new(size) { rand(max) }
end
=> 1.9114e-05
Here is one solution:
Suppose you want these random numbers to be between r_min and r_max. For each element in your list, generate a random number r, and make list[i]=list[i-1]+r. This would give you random numbers which are monotonically increasing, guaranteeing uniqueness provided that
r+list[i-1] does not over flow
r > 0
For the first element, you would use r_min instead of list[i-1]. Once you are done, you can shuffle the list so the elements are not so obviously in order.
The only problem with this method is when you go over r_max and still have more elements to generate. In this case, you can reset r_min and r_max to 2 adjacent element you have already computed, and simply repeat the process. This effectively runs the same algorithm over an interval where there are no numbers already used. You can keep doing this until you have the list populated.
As far as it is nice to know in advance the maxium value, you can do this way:
class NoLoopRand
def initialize(max)
#deck = (0..max).to_a
end
def getrnd
return #deck.delete_at(rand(#deck.length - 1))
end
end
and you can obtain random data in this way:
aRndNum = NoLoopRand.new(10)
puts aRndNum.getrnd
you'll obtain nil when all the values will be exausted from the deck.
Method 1
Using Kent's approach, it is possible to generate an array of arbitrary length keeping all values in a limited range:
# Generates a random array of length n.
#
# #param n length of the desired array
# #param lower minimum number in the array
# #param upper maximum number in the array
def ary_rand(n, lower, upper)
values_set = (lower..upper).to_a
repetition = n/(upper-lower+1) + 1
(values_set*repetition).sample n
end
Method 2
Another, possibly more efficient, method modified from same Kent's another answer:
def ary_rand2(n, lower, upper)
v = (lower..upper).to_a
(0...n).map{ v[rand(v.length)] }
end
Output
puts (ary_rand 5, 0, 9).to_s # [0, 8, 2, 5, 6] expected
puts (ary_rand 5, 0, 9).to_s # [7, 8, 2, 4, 3] different result for same params
puts (ary_rand 5, 0, 1).to_s # [0, 0, 1, 0, 1] repeated values from limited range
puts (ary_rand 5, 9, 0).to_s # [] no such range :)

Resources