Get total number of ranges of a given length in an array - ruby

I have an array total of 12 elements, each element represents and int. For instance total[0] = 1. I have another array remaining that is total - occupied spaces. remaining will have fewer elements that total.
I want to write a method that can look in total for instances where there are >= size gaps between consecutive ints in the array. For example:
If `foo.total = [1,2,6,7,8,9,]`
then when I call `foo.number_of_slots_available(3)`
I get `2` (because 3,4,5 is not included and 10,11,12 is not included)
Here are the beginnings of my method:
def number_of_slots(size)
total_array = (1..12).to_a
occupied_spaces = some_map.to_a
remaining_array = total_array - occupied_spaces
return ????
end

Enumerable#chunk is the good way to go. Look below.
arr = [1,2,6,7,8,9]
rng = (1..12).to_a
rng.chunk { |i| arr.include? i }.to_a
# => [[true, [1, 2]],
# [false, [3, 4, 5]],
# [true, [6, 7, 8, 9]],
# [false, [10, 11, 12]]]
rng.chunk { |i| arr.include? i }.count{|j| j[0] == false}
# => 2
Edit
"I want to write a method that can look in total for instances where there are >= size gaps between consecutive ints in the array"
arr = [1,2,3,6,7,8,9,10,11]
rng = (1..15).to_a
rng.chunk { |i| arr.include? i }.to_a
# => [[true, [1, 2, 3]],
# [false, [4, 5]],
# [true, [6, 7, 8, 9, 10, 11]],
# [false, [12, 13, 14, 15]]]
rng.chunk { |i| arr.include? i }.count{|j| j[0] == false and j[1].size >= 3 }
# => 1
rng.chunk { |i| arr.include? i }.count{|j| j[0] == false and j[1].size >= 2 }
# => 2
# J[1] is the array,whose size is the actual gap size.

If total is sorted is a simple algorithm and should be something like this (I might have some syntax errors):
def containsGaps(total, gap)
int i = 0;
int count = 0;
while i < total.length -1 do
if total[i+1] - total[i] >= gap then count++;
end
return count;
end
And your return might be:
return containsGaps(total_array, size);

Here is a way I found of doing it. I modified the method a bit adding in the array to be passed along with the size.
#!/usr/bin/ruby
arr = [1,2,6,7,8,9]
bar = [1,2,3,6,7,10]
def number_of_slots(arr, size)
count = 0
range = (1..12).to_a
# arr.sort! (if the array is not always sorted)
range.each_cons(size) do |chunk|
if (chunk & arr).size == 0
count += 1
end
end
count
end
puts number_of_slots(arr, 3)
puts number_of_slots(bar, 2)
Output:
2
3

Related

Algorithm for array with `while` or `until` loop

I have:
array = [1, 4, -1, 3, 2]
I want a new array that follows the following logic:
First element is located at index 0, so it is 1.
Second element is located at index 1 (because value for index 0 was 1).
Third element is located at index 4, so it is 2.
And so on until the loop meets value -1, which is the last value, and it should brake.
The new array should be:
[1, 4, 2, -1]
I have:
def task(a)
array = []
a.each_with_index do |v, i|
result = a[i]
until a[i] == -1
array << a[result]
end
end
puts result
end
As others say, you need to change the index in your loop. Also, if you want -1 in the result, you should exit at bottom. And with_index will give you indices in order, which is not what you want here. This will do what you want:
def task(a)
i = 0
array = []
begin
i = a[i]
array << i
end until i == -1
array
end
p task([1, 4, -1, 3, 2])
# => [1, 4, 2, -1]
until a[i] == -1
array << a[result]
end
This code is looping eternally - there is nothing to change i .
As discussed in the comments, you are looping through the array which is not what you require.
You could use a recursive method to handle jumping from one element to another based on previous value. Consider the following:
arr = [1, 4, -1, 3, 2]
def task(arr, n=0, result=[])
if arr[n] == -1
return result + [-1]
end
r = arr[n]
task(arr, r, result + [r])
end
puts task(arr)
input_array = [1, 4, -1, 3, 2]
last_valid_index = input_array.find_index { |entry| entry < 0 }
first_element = input_array.first
last_element = input_array[last_valid_index]
middle_elements = (1..last_valid_index).map { |i| input_array[input_array[i-1]]}
output_array = [first_element] + middle_elements + [last_element]
p output_array
# => [1, 4, 2, -1]
you could to most of it on one line like so, but I think the more verbose version is more self documenting.
input_array = [1, 4, -1, 3, 2]
last_valid_index = input_array.find_index { |entry| entry < 0 }
output_array = [input_array.first] + (1..last_valid_index).map { |i| input_array[input_array[i-1]]} + [input_array[last_valid_index]]
p output_array
# => [1, 4, 2, -1]
I'd suggest this option, just to avoid infinite loops or index out range:
i, ary = 0, [array[0]]
array.size.times do
break if array[i] == -1 or array[i] > array.size - 1
i = array[i]
ary << array[i]
end
ary #=> [1, 4, 2, -1]
An infinite loop happens for example when array = [1, 4, -1, 0, 3].
Index out of range can happen when array = [1, 4, 6, 3, 2]

Ruby array five_sort algorithm

I'm trying to solve a problem called five_sort that accepts an array of integers as the argument and places all the fives at the end of the array and leaves all of the other numbers unsorted. For example, [1,2,5,3,2,5,5,7] would be sorted as [1,2,3,2,7,5,5,5].The rules for the problem state that only a while loops can be used and no other methods can be called on the array except [] and []=. Here is my current code:
def five_sort(array)
sorted = false
while sorted == false
idx = 0
while idx < array.length
if array[idx] == 5
array[idx], array[idx + 1] = array[idx + 1], array[idx]
end
idx += 1
end
sorted = true
end
array
end
When running it, it is just in a continuous loop but I can't find out how to fix it. I know that if I just run the second while loop without the while sorted loop, the array would only run once and the fives would only switch places once and the loop would be over. But I don't know how to run the second while loop and stop it once all the fives are at the end.
Can anyone help me figure this one out?
Just a simple O(n) time and O(1) space solution, using a write-index and a read-index.
w = r = 0
while array[w]
r += 1 while array[r] == 5
array[w] = array[r] || 5
w += 1
r += 1
end
While a couple of people have posted alternative approaches, which are all good, I wanted to post something based on your own code to reassure you that you had got pretty close to a solution.
I've added comments to explain the changes I've made:
def five_sort(array)
sorted = false
while sorted == false
idx = 0
# use did_swap to keep track of if we've needed to swap any numbers
did_swap = false
# check if next element is nil as alternative to using Array#length
while array[idx + 1] != nil
# it's only really a swap if the other entry is not also a 5
if array[idx] == 5 and array[idx + 1] != 5
array[idx], array[idx + 1] = array[idx + 1], array[idx]
did_swap = true
end
idx += 1
end
# if we've been through the array without needing to make any swaps
# then the list is sorted
if !did_swap
sorted = true
end
end
array
end
Your array is becoming longer at each loop:
array = [1,2]
array[1], array[2] = array[2], array[1]
puts array.length
Outputs 3.
What you need is to not swap if idx = array.length - 1
if (array[idx] == 5)
array[idx], array[idx+1] = array[idx+1], array[idx] if idx != array.length - 1
end
def five_sort(arr)
i = 0
cnt = 0
while arr[i]
if arr[i] == 5 && arr[i+1]
arr[i..i] = []
cnt += 1
else
i += 1
end
end
cnt.times { arr[-1,2] = [arr[-1],5] }
arr
end
arr = [1,5,3,5,6]
five_sort arr
#=> [1, 3, 6, 5, 5]
arr
#=> [1, 3, 6, 5, 5] # confirms arr is mutated
five_sort [5,5,5,3,6]
#=> [3, 6, 5, 5, 5]
five_sort [5,5,5,5,5]
#=> [5, 5, 5, 5, 5]
five_sort [1,2,3,4,6]
#=> [1, 2, 3, 4, 6]
five_sort []
#=> []
Notes:
As required by the spec, the only methods invoked on arr are [] and []= and no other arrays are created.
if i indexes the last element of the array, arr[i+1] equals nil.
arr[i..i] = [] removes the elementarr[i] from arr.
arr[-1,2] = [arr[-1],5] appends a 5 to arr.

Given an array, replace each prime number with the next prime number

So
#[11,13,17,23] => [13,17,19,29]
if the number isn't prime, then the function is just returning the number
#[11,8,2,24] => [13,8,3,24]
I'm having so much trouble with the very last number (29), for some reason, it's going into an infinite loop. I feel like there is such a simple solution, but it's just escaping me..
def next_prime(arr)
new_arr = []
arr.each do |num|
#puts num
if prime?(num)
p = false
i = num + 2
while !p
p = prime?(i)
i += 2
end
new_arr << i
else
new_arr << num
end
end
new_arr
end
EDIT: here is the prime function
def prime?(num)
if num ==1
return false
elsif num < 3
return true
elsif num % 2 == 0 || num % 3 == 0
return false
end
i = 5
while i*i <= num
if num % i == 0 || num % (i + 2) == 0
return false
end
i += 6
end
return true
end
The first array works decently for me, even the 29, except for the fact that everything is 2 higher than it should be, because you add 2 after you check if you have a prime which can be fixed by a slight alteration to the code:
if prime?(num)
p = false
i = num
while !p
i += 2
p = prime?(i)
end
new_arr << i
The only infinite loop I encounter is when you hit 2 in the second array, because to check for primes, you just keep incrementing by 2, and so you end up checking every multiple of 2 to see if it's prime. Naturally you never find a prime multiple of 2. To fix this, instead of incrementing by 2 before checking for the next prime, if you increment by 1 would work, you just need to check twice as many numbers:
if prime?(num)
p = false
i = num
while !p
i += 1
p = prime?(i)
end
new_arr << i
your last problem is that your prime? function returns false for 3, which can be fixed by changing:
elsif num <= 3
return true
and now your 2 samples yield:
[11, 13, 17, 23] => [13, 17, 19, 29]
[11, 8, 2, 24] => [13, 8, 3, 24]
Similar to #Cary Swoveland answer, but more idiomatic Ruby IMHO.
require 'prime'
def next_primes(ary)
ary.map { |candidate| candidate.prime? ? next_prime(candidate) : candidate }
end
def next_prime(previous_prime)
all_primes = Prime.each
all_primes.find { |prime| prime > previous_prime }
end
next_primes([11,8,2,24]) # => [13, 8, 3, 24]
next_primes([11,13,17,23]) # => [13, 17, 19, 29]
I've assumed the array arr is sorted. If it isn't, save the original indices of the sorted values, which are then used to reorder the elements of the array of values containing prime and non-prime numbers. For example, if
arr = [57, 13, 28, 6]
then
indices = arr.each_with_index.sort.map(&:last)
#=> [3, 1, 2 0]
The steps of the the main method are as follows.
Step 1: create an empty array a that will be returned by the method.
Step 2: create an enumerator, enum, that generates an infinite sequence of prime numbers (2, 3, 5, 7,..). Generate the first prime m = enum.next #=> 2.
Step 3: create an enumerator, earr, that generate the elements of the given array arr.
Step 4: consider the next element of the array, x = earr.next, which is initially the first element of the array. When there are no more elements of the array, break from the loop and return the array a.
Step 5: if x is not prime save it to an array a (a << x) and repeat step 4; else go to Step 6.
Step 6: (x is prime) if x < m, save m to the array a and go to
Step 4; else (i.e., m <= x), obtain the next prime (m = enum.next) and repeat this step.
require 'prime"
def next_primes(arr)
enum = Prime.each
m = enum.next
earr = arr.to_enum
x = earr.next
a = []
loop do
if x.prime?
if x < m
a << m
x = earr.next
else
m = enum.next
end
else
a << x
x = earr.next
end
end
a
end
next_primes([2, 6, 7, 23, 100])
#=> [3, 6, 11, 29, 100]
next_primes([2, 6, 7, 7, 100])
#=> [3, 6, 11, 11, 100]
Solution to find the next prime number if it is a prime number if not return the same number:
def is_prime(num)
if num<2
return false
end
(2...num).each do |i|
if num%i == 0
return false
end
end
return true
end
def next_prime(arr)
new_arr = []
arr.each do |num|
if is_prime(num)
p = false
i = num
while !p
i += 1
p = is_prime(i)
end
new_arr<<i
else
new_arr<<num
end
end
return new_arr
end
print next_prime([2,3,4,5])
puts
print next_prime([2, 6, 7, 23, 100]) #[3, 6, 11, 29, 100]

error in modulus method in ruby

I am trying to write a method that takes in an array as an argument and returns an array of the numbers in the argument that are have both an even index number and an even value. I am not sure why, but it is giving me the error "undefined method %" in line 5. Can someone explain how I can fix this?
def odd_value_and_position(array)
newArray=[] #create new array
i=0 #i=0
while i <= array.length #loop while
newArray.push(array[i]) if array[i] % 2 != 0
i = i + 2
end
return newArray
end
puts odd_value_and_position([0,1,2,3,4,5])
Another way to do this:
def evens arr
arr.select.with_index { |e,i| e.even? && i.even? }
end
evens [0,1,2,3,4,5] #=> [0,2,4]
When i is equal to array.length, array[i] is nil.
What is nil % 2? It is undefined.
def odd_value_and_position(array)
newArray=[] #create new array
i=0 #i=0
while i < array.length #loop while
newArray.push(array[i]) if array[i] % 2 != 0
i = i + 2
end
return newArray
end
puts odd_value_and_position([0,1,2,3,4,5]) #=> []
puts odd_value_and_position([1,2,3,4,5]) #=> [1,3,5]
Due to the fact that the first element in a Ruby Array has 0 as index, I'm not sure you get the result you expected. See examples in code.
A more Rubyish example would be :
def odd_value_and_position(array)
array.select.with_index(1){|x,i| x.odd? && i.odd?}
end
puts odd_value_and_position([1,2,3,4,5]) #=> [1,3,5]
If I understand the question right, I'd go with something like:
def some_method_name(array)
array.select.with_index { |*ij|
ij.all?(&:even?)
}
end
puts some_method_name([0, 1, 2, 3, 4, 5, 10, 13, 21, 22, 30])
# >> 0
# >> 2
# >> 4
# >> 10
# >> 30
Here's what it's doing:
def some_method_name(array)
array.select.with_index { |*ij|
ij # => [0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [10, 6], [13, 7], [21, 8], [22, 9], [30, 10]
ij.all?(&:even?) # => true, false, true, false, true, false, true, false, false, false, true
}
end
puts some_method_name([0, 1, 2, 3, 4, 5, 10, 13, 21, 22, 30])
# >> 0
# >> 2
# >> 4
# >> 10
# >> 30
There are a couple problems with the original code.
Using while loops easily leads to problems with off-by-one errors, or loops that never trigger, or loops that never end.
To combat that in Ruby, we use each and map, select, reject or similar iterators to loop over the array, and process each element in turn, then base the logic on that.
array.select is looking at each element and applying the logic in the block, looking for "truthy" results. with_index adds the index of the iteration as a second value passed into the block. *id turns the two values being passed in into an array, making it easy to apply all? and its even? test. If even? returns true to both then all? triggers and returns true again which signals to select to return that element of the array.

Best way to partition a sorted array into arrays of contiguous numbers?

Is there an easy way or a method to partition an array into arrays of contiguous numbers in Ruby?
[1,2,3,5,6,8,10] => [[1,2,3],[5,6],[8],[10]]
I can make some routine for that but wonder if there's a quick way.
Sam
I like to inject:
numbers = [1, 2, 3, 5, 6, 8, 10]
contiguous_arrays = []
contiguous_arrays << numbers[1..-1].inject([numbers.first]) do |contiguous, n|
if n == contiguous.last.succ
contiguous << n
else
contiguous_arrays << contiguous
[n]
end
end
#=> [[1, 2, 3], [5, 6], [8], [10]]
A smörgåsbord of approaches, with:
arr = [1,2,3,5,6,8,10]
#1
# If subarray is empty or the current value n is not the last value + 1,
# add the subarray [n] to the collection; else append the current value
# to the last subarray that was added to the collection.
arr.each_with_object([]) { |n,a|
(a.empty? || n != a.last.last+1) ? a << [n] : a[-1] << n }
#=> [[1, 2, 3], [5, 6], [8], [10]]
#2
# Change the value of 'group' to the current value n if it is the first
# element in arr or it is not equal to the previous element in arr + 1,
# then 'chunk' on 'group' and extract the result from the resulting chunked
# array.
arr.map.with_index do |n,i|
group = n if i == 0 || n != arr[i-1] + 1
[n, group]
end.chunk(&:last)
.map { |_,c| c.map(&:first) }
#=> [[1, 2, 3], [5, 6], [8], [10]]
#3
# If n is the last element of arr, append any number other than n+1 to
# a copy of arr and convert to an enumerator. Step though the enumerator
# arr.size times, adding the current value to a subarray b, and using
# 'peek' to see if the next value of 'arr' equals the current value plus 1.
# If it does, add the subarray b to the collecton a and set b => [].
enum = (arr+[arr.last]).to_enum
a, b = [], []
arr.size.times do
curr = enum.next
b << curr
(a << b; b = []) unless curr + 1 == enum.peek
end
end
a
#=> [[1, 2, 3], [5, 6], [8], [10]]
#4
# Add elements n of arr sequentially to an array a, each time first inserting
# an arbitrary separator string SEP when n does not equal the previous value
# of arr + 1, map each element of a to a string, join(' '), split on SEP and
# convert each resulting array of strings to an array of integers.
SEP = '+'
match_val = arr.first
arr.each_with_object([]) do |n,a|
(a << SEP) unless n == match_val
a << n
match_val = n + 1
end.map(&:to_s)
.join(' ')
.split(SEP)
.map { |s| s.split(' ').map(&:to_i) }
#=> [[1, 2, 3], [5, 6], [8], [10]]
All of the above methods work when arr contains negative integers.
arr = [1,2,3,5,6,8,10]
prev = arr[0]
result = arr.slice_before { |e|
prev, prev2 = e, prev
e != prev2.succ
}.entries
p result
Not very original, lifted right out of the Ruby docs actually.
Another method with enumerator:
module Enumerable
def split_if
enum = each
result = []
tmp = [enum.peek]
loop do
v1, v2 = enum.next, enum.peek
if yield(v1, v2)
result << tmp
tmp = [enum.peek]
else
tmp << v2
end
end
result
end
end
[1,2,3,5,6,8,10].split_if {|i,j| j-i > 1}
Or:
class Array
def split_if(&block)
prev_element = nil
inject([[]]) do |results, element|
if prev_element && block.call(prev_element, element)
results << [element]
else
results.last << element
end
prev_element = element
results
end
end
end
Just do it iteratively.
x = [1,2,3,5,6,8,10]
y = []; z = []
(1..x.length - 1).each do |i|
y << x[i - 1]
if x[i] != x[i-1] + 1
z << y
y = []
end
end
y << x[x.length - 1]
z << y
z
# => [[1, 2, 3], [5, 6], [8], [10]]

Resources