I am trying to write a function that gives you the number of numbers need to complete a consecutive array. For example if we have the array [3,5,7] the function should return 2 (i.e. 4,6). I have come up with the code below but it gives me the following error? Any ideas why? Thanks!
def consecutive(*arr)
sorted = arr.sort
current_count = 0
sorted.each_index do |i|
next if i == sorted.length
difference = arr[i+1] - arr[i] - 1
current_count += difference
end
current_count
end
And this is the error:
undefined method `-' for nil:NilClass
(repl):9:in `block in Consecutive'
(repl):6:in `each_index'
(repl):6:in `Consecutive'
(repl):16:in `<main>'
If arr is your array, you can do this:
arr = [3,1,5,7,8]
f,l = arr.minmax
#=> [1, 8]
l-f+1 - arr.size
#=> 3
arr[i + 1] is going to be nil on your final pass through each_index. You can use each_cons(2) instead and have it deal with the indices for you:
def Consecutive(arr)
sorted = arr.sort
current_count = 0
sorted.each_cons(2) do |a, b|
difference = b - a - 1
current_count += difference
end
current_count
end
Consecutive([3,5,7])
=> 2
Related
The below code is my newbie take on a bubble sort method.
#For each element in the list, look at that element and the element
#directly to it's right. Swap these two elements so they are in
#ascending order.
def bubble_sort (array)
a = 0
b = 1
until (array.each_cons(2).all? { |a, b| (a <=> b) <= 0}) == true do
sort = lambda {array[a] <=> array[b]}
sort_call = sort.call
loop do
case sort_call
when -1 #don't swap
a += 1
b += 1
break
when 0 #don't swap
a += 1
b += 1
break
when 1 #swap
array.insert(a,array.delete_at(b))
a += 1
b += 1
break
else #end of array, return to start
a = 0
b = 1
break
end
end
end
puts array.inspect
end
array = [4, 2, 5, 6, 3, 23, 5546, 234, 234, 6]
bubble_sort(array)
I want to be able to alter this method so that it takes a block of code as an argument and uses this to determine how it sorts.
For example:
array = ["hello", "my", "name", "is", "daniel"]
bubble_sort(array) {array[#a].length <=> array[#b].length}
(When I've tried this I've turned a and b into instance variables throughout the code.)
I have tried using yield but I get undefined method 'length' for nil:NilClass once the end of the array is reached. I've tried adding in things such as
if array[#b+1] == nil
#a = 0
#b = 1
end
This helps but I still end up with weird problems like infinite loops or not being able to sort more than certain amount of elements.
Long story short, I have been at this for hours. Is there a simple way to do what I want to do? Thanks.
The way you're calling your lambda is a bit odd. It's actually completely unnecessary. I refactored your code and cleaned up a bit of the redundancy. The following works for me:
def sorted?(arr)
arr.each_cons(2).all? { |a, b| (a <=> b) <= 0 }
end
def bubble_sort (arr)
a = 0
b = 1
until sorted?(arr) do
# The yield call here passes `arr[a]` and `arr[b]` to the block.
comparison = if block_given?
yield(arr[a], arr[b])
else
arr[a] <=> arr[b]
end
if [-1, 0, 1].include? comparison
arr.insert(a, arr.delete_at(b)) if comparison == 1
a += 1
b += 1
else
a = 0
b = 1
end
end
arr
end
sample_array = [4, 2, 5, 6, 3, 23, 5546, 234, 234, 6]
# Sanity check:
100.times do
# `a` is the value of `arr[a]` in our function above. Likewise for `b` and `arr[b]`.
print bubble_sort(sample_array.shuffle) { |a, b| a <=> b }, "\n"
end
EDIT
A cleaner version:
# In place swap will be more efficient as it doesn't need to modify the size of the arra
def swap(arr, idx)
raise IndexError.new("Index #{idx} is out of bounds") if idx >= arr.length || idx < 0
temp = arr[idx]
arr[idx] = arr[idx + 1]
arr[idx + 1] = temp
end
def bubble_sort(arr)
loop do
sorted_elements = 0
arr.each_cons(2).each_with_index do |pair, idx|
comparison = if block_given?
yield pair.first, pair.last
else
pair.first <=> pair.last
end
if comparison > 0
swap(arr, idx)
else
sorted_elements += 1
end
end
return arr if sorted_elements >= arr.length - 1
end
end
# A simple test
sample_array = [4, 2, 2, 2, 2, 2, 5, 5, 6, 3, 23, 5546, 234, 234, 6]
sample_str_array = ["a", "ccc", "ccccc"]
100.times do
print bubble_sort(sample_array.shuffle) { |a, b| a <=> b }, "\n"
print bubble_sort(sample_str_array.shuffle) { |a, b| a.length <=> b.length }, "\n"
end
You're not too far off. Just a few things:
Make your function take a block argument
def bubble_sort (array, &block)
Check to see if the user has provided a block
if block_given?
# Call user's comparator block
else
# Use the default behavior
end
Call the user's comparator block
block.call(a, b)
In the user-provided block, accept block params for the elements to compare
bubble_sort(array) {|a,b| a.length <=> b.length}
That should put you in the right ballpark.
Details of the problem: To find if any combination of the array adds to the largest number found in the array.
Here are the steps I am trying to implement:
Extract the largest number from the array
Create a new array of
all the potential combinations that could be added by using
.combination
Test to see if any of these combinations equals the largest number in the original array.
Status: So far, I am just receiving an unexpected end error for the last end in the code. (I have found different answers online on how to solve the subset sums problem in Ruby, but would like to figure out how to solve it using the logic I have used so far.)
Any help would be great!
def subset_sum(sums)
largest_number = subset_sum.sort.reverse[0]
array_without_largest = subset_sum.sort.reverse[1..-1]
full_combination = []
i = 0
while i <= array_without_largest.length
full_combination = full_combination + array_without_largest.combination(i).to_a.to_s
i += 1
end
j = 0
while j <= full_combination.length
return true if full_combination[j].inject { |sum, x| sum + x} == largest_number
j += 1
end
end
return false
end
puts subset_sum(1,2,3,4,10)
puts subset_sum(-1,-3,3,9,8)
Consider this:
def any_subset_adds_to_max?(array)
sub_array = array - [array.max]
every_combination = (1..sub_array.length).flat_map { |n| sub_array.combination(n).to_a }
every_combination.any? { |combination| combination.reduce(:+) == array.max }
end
[
[1, 2, 3, 4, 10],
[-1, -3, 3, 9, 8]
].map { |test_array| any_subset_adds_to_max? test_array } # => [true, false]
Here is the closest example of the code that I could do while maintaining the originality. It works and I appreciate the help!
def subset_sum(sums)
largest_number = sums.max
array_without_largest = sums - [largest_number]
full_combination = []
array_without_largest.size.times do |i|
full_combination << array_without_largest.combination(i+1).to_a
end
full_combination.flatten!(1)
full_combination.size.times do |i|
return true if full_combination[i].inject(:+) == largest_number
end
false
end
I see that there are better ruby bubble sort codes already posted in places such as here:
Using the Bubble sort method for an array in Ruby
But I am having trouble debugging my current code and would appreciate some help with figuring out why my code does not work. Thanks.
def bubble_sort(arr)
original = arr
x = 0
while x < arr.count - 1
if arr[x] < arr[x + 1]
y = arr[x + 1]
arr[x + 1] = arr[x]
arr[x] = y
end
x += 1
end
if original == arr
return arr
else
return bubble_sort(arr)
end
end
One of the problems is this:
original = arr
You expect original to a copy of arr in its current state, right? Well, no. They will point to the same array. That's why your function will never recurse here:
if original == arr
return arr
else
return bubble_sort[arr]
end
To copy the array, use dup
original = arr.dup
Four issues :
bubble_sort[arr] does not work - you should call bubble_sort(arr)
original == arr - will always be true, since you assigned it to arr before - you should have assigned it using dup - original = arr.dup
arr[x] < arr [x+1] will create an array sorted in reverse order...
you should change the local copy rather than the one you got as parameters - result = arr.dup rather than original = arr.dup
The code after the above fixes:
def bubble_sort(arr)
result = arr.dup
x = 0
while x < result.count - 1
if result[x] > result[x + 1]
y = result[x + 1]
result[x + 1] = result[x]
result[x] = y
end
x += 1
end
if arr == result
return result
else
return bubble_sort(result)
end
end
bubble_sort([1,3,5,2,4])
# => [1, 2, 3, 4, 5]
How do I calculate the median of an array of numbers using Ruby?
I am a beginner and am struggling with handling the cases of the array being of odd and even length.
Here is a solution that works on both even and odd length array and won't alter the array:
def median(array)
return nil if array.empty?
sorted = array.sort
len = sorted.length
(sorted[(len - 1) / 2] + sorted[len / 2]) / 2.0
end
Similar to nbarraille's, but I find it a bit easier to keep track of why this one works:
class Array
def median
sorted = self.sort
half_len = (sorted.length / 2.0).ceil
(sorted[half_len-1] + sorted[-half_len]) / 2.0
end
end
half_len = number of elements up to and including (for array with odd number of items) middle of array.
Even simpler:
class Array
def median
sorted = self.sort
mid = (sorted.length - 1) / 2.0
(sorted[mid.floor] + sorted[mid.ceil]) / 2.0
end
end
If by calculating Median you mean this
Then
a = [12,3,4,5,123,4,5,6,66]
a.sort!
elements = a.count
center = elements/2
elements.even? ? (a[center] + a[center+1])/2 : a[center]
def median(array) #Define your method accepting an array as an argument.
array = array.sort #sort the array from least to greatest
if array.length.odd? #is the length of the array odd?
array[(array.length - 1) / 2] #find value at this index
else array.length.even? #is the length of the array even?
(array[array.length/2] + array[array.length/2 - 1])/2.to_f
#average the values found at these two indexes and convert to float
end
end
More correct solution with handling edge cases:
class Array
def median
sorted = self.sort
size = sorted.size
center = size / 2
if size == 0
nil
elsif size.even?
(sorted[center - 1] + sorted[center]) / 2.0
else
sorted[center]
end
end
end
There is a specs to prove:
describe Array do
describe '#median' do
subject { arr.median }
context 'on empty array' do
let(:arr) { [] }
it { is_expected.to eq nil }
end
context 'on 1-element array' do
let(:arr) { [5] }
it { is_expected.to eq 5 }
end
context 'on 2-elements array' do
let(:arr) { [1, 2] }
it { is_expected.to eq 1.5 }
end
context 'on odd-size array' do
let(:arr) { [100, 5, 2, 12, 1] }
it { is_expected.to eq 5 }
end
context 'on even-size array' do
let(:arr) { [7, 100, 5, 2, 12, 1] }
it { is_expected.to eq 6 }
end
end
end
I like to use Refinements, which is a safe way to Monkey Patch the ruby classes without collateral effects over the system.
The usage become much more cleaner than a new method.
With the Refinements you can monkey patch the Array class, implement the Array#median and this method will only be available inside the scope of the class that is using the refinement! :)
Refinements
module ArrayRefinements
refine Array do
def median
return nil if empty?
sorted = sort
(sorted[(length - 1) / 2] + sorted[length / 2]) / 2.0
end
end
end
class MyClass
using ArrayRefinements
# You can use the Array#median as you wish here
def test(array)
array.median
end
end
MyClass.new.test([1, 2, 2, 2, 3])
=> 2.0
def median(array)
half = array.sort!.length / 2
array.length.odd? ? array[half] : (array[half] + array[half - 1]) / 2
end
*If the length is even, you must add the middle point plus the middle point - 1 to account for the index starting at 0
def median(arr)
sorted = arr.sort
if sorted == []
return nil
end
if sorted.length % 2 != 0
result = sorted.length / 2 # 7/2 = 3.5 (rounded to 3)
return sorted[result] # 6
end
if sorted.length % 2 == 0
result = (sorted.length / 2) - 1
return (sorted[result] + sorted[result+1]) / 2.0 # (4 + 5) / 2
end
end
p median([5, 0, 2, 6, 11, 10, 9])
Here's a solution:
app_arry = [2, 3, 4, 2, 5, 6, 16].sort
# check array isn't empty
if app_arry.empty? || app_arry == ""
puts "Sorry, This will not work."
return nil
end
length = app_arry.length
puts "Array length = #{length}"
puts "Array = #{app_arry}"
if length % 2 == 0
# even number of elements
puts "median is #{(app_arry[length/2].to_f + app_arry[(length-1)/2].to_f)/2}"
else
# odd number of elements
puts "median is #{app_arry[(length-1)/2]}"
end
OUTPUT
Array length = 7
Array = [2, 3, 4, 2, 5, 6, 16]
median is 2
def median(array, already_sorted=false)
return nil if array.empty?
array = array.sort unless already_sorted
m_pos = array.size / 2
return array.size % 2 == 1 ? array[m_pos] : mean(array[m_pos-1..m_pos])
end
There are many ways to do this, but for both performance and reliability, I suggest using the enumerable-statistics library created by Ruby committer mrkn.
https://github.com/mrkn/enumerable-statistics
require 'enumerable/statistics'
ary = [1,2,3,3,4]
ary.mean # => 2.6
ary.median # => 3
I think it's good:
#!/usr/bin/env ruby
#in-the-middle value when odd or
#first of second half when even.
def median(ary)
middle = ary.size/2
sorted = ary.sort_by{ |a| a }
sorted[middle]
end
or
#in-the-middle value when odd or
#average of 2 middle when even.
def median(ary)
middle = ary.size/2
sorted = ary.sort_by{ |a| a }
ary.size.odd? ? sorted[middle] : (sorted[middle]+sorted[middle-1])/2.0
end
I used sort_by rather than sort because it's faster: Sorting an array in descending order in Ruby.
I am new to Ruby and I am trying out the merge sort algorithm as given in Wikipedia
I am getting "comparison of Fixnum with Array failed (ArgumentError)" failed error when comparing the first elements of the left and right array in the merge method. What could be the reason and how do I resolve this problem? Thanks :)
def mergeSort(array)
if array.length == 1
return array
end
middle = array.length/2 - 1
left = array[0..middle]
right = array[middle+1..array.length-1]
left = mergeSort(left)
right = mergeSort(right)
merge(left,right)
end
def merge(left,right)
result = []
while left.length > 0 || right.length > 0
if left.length > 0 && right.length > 0
one = left[0]
two = right[0]
puts ("one's class is #{one.class} two's class is #{two.class} two is #{two}")
if one <= two
result << left.shift
else
result << right.shift
end
elsif left.length > 0
result.push(left)
left = []
else
result.push(right)
right = []
end
end
puts "result is #{result}"
result
end
The error is on these lines:
elsif left.length > 0
result.push(left)
left = []
else
result.push(right)
right = []
end
A simple example should indicate why:
irb(main):067:0> a=[1,2]
=> [1, 2]
irb(main):068:0> b=[3,4]
=> [3, 4]
irb(main):069:0> a.push(b)
=> [1, 2, [3, 4]]
Instead of push(), try concat().