I wrote a Ruby code to get max and min values from an array. The code prints the max value (8) correct but it's not printing the minimum value (2). Please let me know what went wrong in my code.
class MaxMinArray
def MaxMinMethod()
array = [4,2,8,3,5]
maxNo = array[0]
minNo = array[0]
arrayLength = array.length
for i in 1..arrayLength
if array[i].to_i > maxNo
maxNo = array[i]
end
if array[i].to_i < minNo
minNo = array[i]
end
end
puts "Maximum no. in the given array: " + maxNo.to_s
puts "Minimum no. in the given array: " + minNo.to_s
end
end
MaxiMinArrayObj = MaxMinArray.new
MaxiMinArrayObj.MaxMinMethod()
It is the combination of two things.
First, you iterated over for i in 1..arrayLength, which iterates past the last element in array. After the last element, array[i] is nil.
Second, you have the condition if array[i].to_i < minNo, which can be satisfied even if array[i] is not a number.
Because of that, the nil returned by array[i] after the last element satisfies the condition due to nil.to_i being 0, and that nil is assigned to minNo.
I realize you're trying to learn how to code, but, as you do so, it's also important to learn to take advantage of pre-existing solutions. Reinventing wheels will waste your time debugging code.
I'd write the code like:
def max_min(ary)
[ary.max, ary.min]
end
max_min([1,2,4]) # => [4, 1]
But, then again, Ruby already has a good minmax method:
[1,2,4].minmax # => [1, 4]
so use it and focus your energy on more interesting things.
If you have to see the values in the opposite order, use:
[1,2,4].minmax.reverse # => [4, 1]
A more verbose/old-school way of doing it is:
FIXNUM_MAX = (2 ** (0.size * 8 - 2) - 1)
FIXNUM_MIN = -(2 ** (0.size * 8 - 2))
def max_min(ary)
return [nil, nil] if ary.empty?
minval = FIXNUM_MAX
maxval = FIXNUM_MIN
ary.each do |i|
minval = i if i < minval
maxval = i if i > maxval
end
[maxval, minval]
end
max_min([1,2,4]) # => [4, 1]
[1,2,4].minmax.reverse # => [4, 1]
That simply loops over the array, checks each value to see if it's either smaller or larger than the last minimum or maximum value, and, if so, remembers it. Once the array is exhausted the values are returned. It's a lot more concise because using each removes a lot of the hassle of trying to walk the array using index values. We almost never use for in Ruby, especially to walk through an array.
(Technically Ruby can hold values well beyond 4611686018427387903 and -4611686018427387904, which are what FIXNUM_MAX and FIXNUM_MIN are, but those suffice for most things we want to do.)
It's not a good practice to print inside methods as long as you might want to use the results for something else.
Also Ruby comes with all sorts of magic methods to get the maximum and minimum of an array:
results = [5, 23, 43, 2, 3, 0].minmax
puts "Maximum no. in the given array: " + results[1]
puts "Minimum no. in the given array: " + results[0]
You should iterate from 1 to arrayLength - 1 (it's an index of the last element). You can use three dots for this:
for i in 1...arrayLength
If I were not allowed to used Ruby's minmax method, than I would do it probably like this:
array = [4,2,8,3,5]
min, max = nil, nil
array.each do |element|
min = element if min.nil? || element < min
max = element if max.nil? || max < element
end
puts [min, max]
# => [2, 8]
I used this expression for the min and max within ruby, it's a stretch but it works
class RubyMinMax
def self.min_function(array=[])
puts "my array is the following #{array}"
puts "the length of the array is #{array.length}"
it = 0
while array.length > 1
array.fetch(it).to_i > array.fetch(it-1).to_i ? array.delete_at(it) : array.delete_at(it-1)
it = array.length-1
end
print array[0]
end
def self.max_function(array=[])
puts "my array is the following #{array}"
puts "the length of the array is #{array.length}"
it = 0
while array.length > 1
array.fetch(it).to_i < array.fetch(it-1).to_i ? array.delete_at(it) : array.delete_at(it-1)
it = array.length-1
end
print array[0]
end
end
RubyMinMax.min_function([18, 19, 17])
RubyMinMax.max_function([18, 19, 17])
In the simplest way you can use max and min method of array.
:001 > [1,4,1,3,4,5].max
=> 5
:002 > [1,4,1,3,4,5].min
=> 1
And if your array may contain nil the first compact it the use min max
For example
:003 > [1,4,1,3,4,5,nil].compact
=> [1, 4, 1, 3, 4, 5]
:004 > [1,4,1,3,4,5].max
=> 5
:005 > [1,4,1,3,4,5].min
=> 1
Related
I am working on a school exercise on Quick Sort.
I have succeded to do the first exercise which is
Challenge
Given an array 'array' and a number 'p' in the first cell in the
array, can you partition the array so that all elements greater than
'p' is to the right of it and all the numbers smaller than 'p' are to
it's left? For example, if given the following as input:
4 5 3 9 1 The first number 4 is the pivot, so you should put the
smaller numbers to the left, and the larger to the right, and output:
3 1 4 5 9 The array should otherwise remain in the same order.
Can you write code to partition an array?
Example p partition([4, 5, 3, 9, 1])
=> [3, 1, 4, 5, 9]
My code for the above in ruby is
def partition(array)
# write your code here
pivot = array.shift()
base = [pivot]
left = []
right = []
array.each { |e| if e < pivot
left.push(e)
else
right.push(e)
end
}
left + base + right
end
p partition([4, 5, 3, 9, 1])
# => [3, 1, 4, 5, 9]
The Challenge for which I am raising this Question is
The function should output like this
p some_function_name([5, 8, 1, 3, 7, 10, 2])
# => 2 3
# 1 2 3
# 7 8 10
# 1 2 3 5 7 8 10
I am trying for the last 36hrs how to apply the partition code above recursively on this challenge. During my 36hrs of research on the Quick Sort algorithm, I can make the code to give the result of a sorted array, but this challenge is asking to provide prints at certain conditions which I am not able to achieve.
Any help is much appreciated.
This one tried for pivot at end
def partition(array)
# write your code here
pivot = array[-1]
i = -1
j = 0
while j < array.length-1
if array[j] < pivot
i += 1
array[i], array[j] = array[j], array[i]
end
j += 1
end
array.insert(i+1, array.pop)
puts index = i+1
puts (array.take index).join(' ')
puts (array.drop index+1).join(' ')
end
partition([5, 8, 1, 3, 7, 10, 2])
this one, I am not able to find a condition for terminating recursive function
def partition(array)
# write your code here
pivot = array.shift()
base = [pivot]
left = []
right = []
array.each { |e| if e < pivot
left.push(e)
else
right.push(e)
end
}
left + base + right
if left.length < 2
return
end
partition(left)
end
p partition([5, 8, 1, 3, 7, 10, 2])
p partition([1, 3, 2])
p partition([8, 7, 10])
It's not clear to my why you want partition to be recursive. There is no real natural way to make it recursive in a simple way. You can introduce a recursive helper method, but I don't see that as an improvement. partition really doesn't need to be more complicated than this:
def partition(array, pivot)
return [], [] if array.empty?
array.partition(&pivot.method(:>))
end
If you absolutely must, you can make it recursive like this:
def partition(...) = partition_rec(...)
private def partition_rec(array, pivot, left = [], right = [])
return left, right if array.empty?
first = array.first
rest = array.drop(1)
if first < pivot
partition_rec(rest, pivot, left + [first], right)
else
partition_rec(rest, pivot, left, right + [first])
end
end
With this partition in place, we can easily write our quicksort:
def quicksort(array)
return array if array.length < 2
pivot = array.first
left, right = partition(array.drop(1), pivot)
quicksort(left) + [pivot] + quicksort(right)
end
Now, all we need to do is to also print the result at each recursive call. A simple way to do that would be with Kernel#p, which returns its argument, so we can just insert it without changing the return value:
def quicksort(array)
return array if array.length < 2
pivot = array.first
left, right = partition(array.drop(1), pivot)
p quicksort(left) + [pivot] + quicksort(right)
end
If we need to replicate the exact format of the string as given in the question, then we should use Kernel#puts instead:
def quicksort(array)
return array if array.length < 2
pivot = array.first
left, right = partition(array.drop(1), pivot)
(quicksort(left) + [pivot] + quicksort(right)).tap do |result|
puts result.join(' ')
end
end
Note that there is a big no-no in your code. Here, you are modifying the argument passed into partition:
array.shift()
Same here:
array.insert(i+1, array.pop)
You must not, ever, mutate an argument. In fact, you should avoid mutation at all, as much as possible. The only thing you are allowed to mutate is yourself, i.e. self. But even then, you should be careful.
A problem asks to print all numbers from an array that are greater than 100. When I run this:
array = [3, 123, 433, -77, 56, 200, 99, 101, 6]
index = 0
9.times do
if array[index] > 100 == 0
p array[index]
end
index = index + 1
end
the number 9 is printed. I put in the proper conditional if the number was greater than 100 to get printer. Can anyone help?
Just use
if array[index] > 100
# print
end
9 is the evaluation of your program, it's not a 'print', it's the result of 9.times
Your program doesn't print any of the array values because of that condition
if array[index] > 100 == 0
it should be just
if array[index] > 100
don't know why you add that == 0
array[index] > 100 is evaluted before so all the time you always obtain true == 0 or false == 0, and these are never true. So no prints in your code
about that 9 that come up it's what Aleksei says
The problem's actually really easy to solve if you know the right tools to use:
array = [3,123,433,-77,56,200,99,101,6]
array.select { |n| n > 100 }.each do |n|
puts n
end
Where select can help narrow down lists of numbers. Ruby's Array class has an unusually large number of methods like this that can quickly and easily do a variety of things related to filtering and mapping.
The 9.times part in your code was decoupled from the actual length of the array, something that leads to a whole lot of bugs if you add/remove entries and these two fall out of sync.
Use array iterators whenever possible, like:
array = [3,123,433,-77,56,200,99,101,6]
array.each do |n|
if (n > 100)
puts n
end
end
You have written 9.times which is your array size, which indicates you want to run it for all array elements. So use each block on array.
array.each { |x| puts x if x > 100 }
Try to understand why there is no role of index of any array element in above.
Select returns its own array. So we can just print the return value.
puts array.select{|x| x > 99}
I'm doing The Odin Project. The practice problem is: create a merge sort algorithm using recursion. The following is modified from someone's solution:
def merge_sort(arry)
# kick out the odds or kick out of the recursive splitting?
# I wasn't able to get the recombination to work within the same method.
return arry if arry.length == 1
arry1 = merge_sort(arry[0...arry.length/2])
arry2 = merge_sort(arry[arry.length/2..-1])
f_arry = []
index1 = 0 # placekeeper for iterating through arry1
index2 = 0 # placekeeper for iterating through arry2
# stops when f_arry is as long as combined subarrays
while f_arry.length < (arry1.length + arry2.length)
if index1 == arry1.length
# pushes remainder of arry2 to f_arry
# not sure why it needs to be flatten(ed)!
(f_arry << arry2[index2..-1]).flatten!
elsif index2 == arry2.length
(f_arry << arry1[index1..-1]).flatten!
elsif arry1[index1] <= arry2[index2]
f_arry << arry1[index1]
index1 += 1
else
f_arry << arry2 [index2]
index2 += 1
end
end
return f_arry
end
Is the first line return arry if arry.length == 1 kicking it out of the recursive splitting of the array(s) and then bypassing the recursive splitting part of the method to go back to the recombination section? It seems like it should then just keep resplitting it once it gets back to that section as it recurses through.
Why must it be flatten-ed?
The easiest way to understand the first line is to understand that the only contract that merge_sort is bound to is to "return a sorted array" - if the array has only one element (arry.length == 1) it is already sorted - so nothing needs to be done! Simply return the array itself.
In recursion, this is known as a "Stop condition". If you don't provide a stop condition - the recursion will never end (since it will always call itself - and never return)!
The result you need to flatten your result, is because you are pushing an array as an element in you resulting array:
arr = [1]
arr << [2, 3]
# => [1, [2, 3]]
If you try to flatten the resulting array only at the end of the iteration, and not as you are adding the elements, you'll have a problem, since its length will be skewed:
arr = [1, [2, 3]]
arr.length
# => 2
Although arr contains three numbers it has only two elements - and that will break your solution.
You want all the elements in your array to be numbers, not arrays. flatten! makes sure that all elements in your array are atoms, and if they are not, it adds the child array's elements to itself instead of the child array:
arr.flatten!
# => [1, 2, 3]
Another you option you might want to consider (and will be more efficient) is to use concat instead:
arr = [1]
arr.concat([2, 3])
# => [1, 2, 3]
This method add all the elements in the array passed as parameter to the array it is called on.
I want to take the first "n" entries which pass the block
a = 1..100_000_000 # Basically a long array
# This iterates over the whole array -- no good
b = a.select{|x| x.expensive_operation?}.take(n)
I want to short circuit the iteration once i've got n entries where 'expensive' condition is true.
What do you suggest? take_while and keep count of n?
# This is the code i have; which i think can be written better, but how?
a = 1..100_000_000 # Basically a long array
n = 20
i = 0
b = a.take_while do |x|
((i < n) && (x.expensive_operation?)).tap do |r|
i += 1
end
end
Ruby 2.0 implements lazy enumerables, for older versions use the gem enumerable-lazy:
require 'enumerable/lazy'
(1..Float::INFINITY).lazy.select(&:even?).take(5).to_a
#=> [2, 4, 6, 8, 10]
It should work with a simple for loop and a break :
a = 1..100_000_000 # Basically a long array
n = 20
selected = []
for x in a
selected << x if x.expensive_operation?
break if select.length == n
end
Given an array
[50,30,0,0,10,0,30,60,0]
I need to replace the zeroes with calculated values to create a 'curve', so for example, between 10 and 30, the zero could be replaced with 20.
I keep thinking there must be a cool ruby way of doing this, but I cant find one. Can anyone help? The solution needs to take into account multiple adjacent zeroes, and zeroes at the start and end of the range.
Anyone any ideas?
The term you seem to be unaware of is interpolation. The Wikipedia article is a good place to start - exactly what algoithm is best suited to you depends on the exact context of your problem so we can't give you the one true answer here.
a =[50,30,0,0,10,0,30,60,0]
a.each_index{|i| a[i] = a[i-1] - ((a[i-2] - a[i-1])/2).to_i if a[i] == 0 && i > 1 }
puts a.inspect # [50, 30, 20, 15, 10, 8, 30, 60, 75]
I can't work out why the last number might be 80 in your spec however? Plus it doesn't work for the first two items in the array.
If there could not be consecutive zeroes, this unintelligible one-liner would do the trick (list is the given list of numbers):
[0, *list, 0].each_cons(3).map { |p, x, n| x == 0 ? (p + n)/2 : x }
Ruby 1.9 only, I think.
def find_consecutive_values( array, value=nil )
raise "Need a value or block to find" unless value || block_given?
start = last = nil
ranges = []
indices = array.each_with_index do |o,i|
if ((block_given? && yield(o)) || o==value)
start = i unless start
last = i
else
ranges << (start..last) if start && last
start = last = nil
end
end
ranges << (start..last) if start && last
ranges
end
def interpolate_zeros( array, round=false )
result = array.dup
find_consecutive_values( array, 0 ).each do |range|
next unless range.first>0 && range.last<(array.length-1)
before = result[range.first - 1]
after = result[range.last + 1]
diff = after - before
size = (range.last - range.first + 2).to_f
range.each_with_index do |i,idx|
value = before + diff * (idx+1)/size
value = value.round if round
result[i] = value
end
end
result
end
p interpolate_zeros( [0,50,30,0,0,10,0,30,60,0], true )
#=> [0, 50, 30, 23, 17, 10, 20, 30, 60, 0]
Just stumbled across this question. There is a ruby gem "interpolator", which just does what you want and probably tons more:
http://interpolator.rubyforge.org.
Here is a short introduction:
http://fabianosoriani.wordpress.com/2010/02/23/ruby-interpolation-with-gem-interpolator/