sorting programme on Ruby - ruby

I've seen a following sorting programme on Ruby but I don't think I fully understand how it actually works:
def sort arr
rec_sort arr, []
end
def rec_sort unsorted, sorted
if unsorted.length <= 0
return sorted
end
smallest = unsorted.pop
still_unsorted = []
unsorted.each do |tested|
if tested < smallest
still_unsorted.push smallest
smallest = tested
else
still_unsorted.push tested
end
end
sorted.push smallest
rec_sort still_unsorted, sorted
end
puts sort ["satoshi", "Bitcoin", "technology", "universe", "smell"]
=> Bitcoin
satoshi
smell
technology
universe
But when I change the first argument of the "rec_sort" method from "still_unsorted" (as indicated above) to "unsorted", the programme gives :
=> Bitcoin
Bitcoin
Bitcoin
Bitcoin
satoshi
I understand that the each loop selects the word "Bitcoin" first (because it would indeed come first when sorted), and "Bitcoin" would be put into the array "sorted". What I dont't quite understand is why there are several "Bitcoin" here, since it should have been excluded from the "unsorted" array in the first iteration of the each loop and, therefore, could not appear in the following iterations, making it impossible for "Bitcoin" to be in the "sorted" array several times.
Could you tell me what makes the two so different?
Any suggestions will be appreciated. Thank you.

The still_unsorted array has the smallest element removed, but the unsorted array only has its last element removed.

as far as I understand its a recursive implementation of bubble sort. and for you confusion unsorted is not being modified except for the statement unsorted.pop but only is being replicated into the still_unsorted except for the smallest element in that array
I am dry running this on [3,1,2] for you
unsorted = [3,1,2]
sm = unsorted.pop # sm = 2 unsorted = [3,1]
still_unsorted = []
#after loop unsorted.each
# still_unsorted = [2,3]
# unsorted = [3,1]
# sm = 1
# sorted = [1]
do the next 2 iterations you'll understand what's happening

Related

How to remove all the elements after a particular index

Given:
n = 2
arr = %w(10 20 30 40 50)
This is a way to remove all the array elements after index n:
arr.delete_if {|num| arr.index(num) > n }
# => ["10", "20", "30"]
Is there any other way to remove all the elements from an array without iterating over and comparing the condition?
Array#[]= operator comes in handy:
arr[n+1 .. -1] = []
In Ruby 2.6, you can also use endless ranges:
arr[n+1 ..] = []
Note that your code is slow, as it searches the array for every element and is thus O(n^2), but also wrong if the array contains repeating elements. For example, with arr = %w(10 20 30 10 20), your code does not change the array. This would be your code, only faster and correct (O(n); though the #[]= solution above is even faster and more straightforward):
arr.delete_if.with_index { |num, idx| idx > n }
You are deleting elements having index greater than n=2 & get rest of array where you can simply get it as,
arr = arr[0..2]
Above will regenerate new array and arr reference will point to it.
Update: going little deep with point claimed by Cary Swoveland in comment,
Array can be updated without initialising new one as follow,
# This one will have same object id or we can say, do not point to new array
arr.replace(arr[0,3])
You can use Array#slice! to remove elements within a certain index range, e.g.:
arr = %w(10 20 30 40 50)
arr.slice!(3..-1) #=> ["40", "50"]
arr #=> ["10", "20", "30"]
Note that slice! also returns the removed portion.

Find two integer elements in array closest to each other in numerical difference - Ruby

This is actually a technical interview question I found.
Given an array, find the two integer elements that are closest to each other. Return the smallest pair if there are more than one [pair with the same difference between them].
I took a shot at this. As you can see, it looks ugly. I'm wondering about alternative, cleaner and more efficient approaches. I didn't do the last part of the question about returning the smallest pair if there are more than one but it works otherwise.
least_diff = 99999 #how to represent infinity?
ans_arr = []
for i in (0...arr.length)
for k in (0...arr.length)
if i != k
curr_diff = (arr[i] - arr[k]).abs
if curr_diff < least_diff
if ans_arr != nil
ans_arr.pop
end
least_diff = curr_diff
ans_arr.push("\(#{arr[i]},#{arr[k]}\)")
end
end
end
end
ans_arr[0]
a = [3, -43, 1, 98, 29, 8, 10, -66]
a.sort.each_cons(2).min_by{|x, y| y - x}
# => [1, 3]
The nice thing about this problem being 1-dimensional is that the closest elements are guaranteed to be adjacent when the array is sorted. So I'd take this approach:
Sort the array.
Scan the array linearly, computing the difference between every adjacent element.
Record the minimum difference at every step — at the end of your list, this is the final answer.
I don't know about Ruby! But I can surely help out with the logic.
For the answer of your question,
How to set least_diff infinity?
-> If it is not infinite then also it will work but for that you have to find the maximum number you can store in least_diff, and assign that value to least_diff.
-> Also what if if curr_diff < least_diff is not true? you must have another part to check if curr_diff == least_diff & else part. (i.e. No condition is matched!)
For answer of the second part of your problem,
-> You can check the smallest pair by checking the sum of the pair.
For example,
Suppose you have pairs, (5,6), (3,4) and (4,8) in your answer. Now according to problem, you have to find (3,4) from the above pairs.
(4+8=12) > (5+6=11) > (3+4=7), so least sum '7' is your answer.
So you can add in your code the following logic.
least_pair_sum = 9999 #Assign the maximum value as I mentioned above.
#in the if part
if curr_diff <= least_diff
if ans_arr != nil
curr_pair_sum = arr[i] + arr[k] #Don't know the syntax! Just see the logic
if curr_pair_sum <= least_pair_sum
ans_arr.push("\(#{arr[i]},#{arr[k]}\)")
end
end
least_diff = curr_diff
#Removed ans_arr.push("\(#{arr[i]},#{arr[k]}\)") so you have only answer in the array
end
Hope it can solve your problem.

Please walk me through this code from ruby monk

def random_select(array, n)
result = []
n.times do
# I do not fully understand how this line below works or why. Thank you
result.push array[rand(array.length)]
end
result
end
You are probably confused by this part:
n.times do
result.push(array[rand(array.length)])
end
n.times says it should loop n times.
result.push says to basically "push" or "put" something in the array. For example:
a = []
a.push(1)
p a #=> [1]
In array[rand(array.length)] , rand(array.length) will produce a random number as an index for the array. Why? rand(n) produces a number from 0 to n-1. rand(5) will produce either 0,1,2,3 or 4, for example.
Arrays use 0-based indexing, so if you have an array, say a = ['x', 'y', 'z'], to access 'x' you do a[0], to access y you do a[1] and so on. If you want to access a random element from a, you do a[rand(array.length)], because a.length in this case is 3, and rand(3) will produce a number that is either 0, 1 or 2. 0 is the smallest index and 2 is the largest index of our example array.
So suppose we call this method:
random_select([6,3,1,4], 2)
Try to see this code from the inside out. When the code reaches this part:
result.push(array[rand(array.length)])
it will first execute array.length which will produce 4. It will then execute rand(array.length) or rand(4) which will get a number between 0 and 3. Then, it will execute array[rand(array.length)] or array(some_random_number_between_0_and_3) which will get you a random element from the array. Finally, result.push(all_of_that_code_inside_that_got_us_a_random_array_element) will put the random element from the array in the method (in our example, it will be either 6, 3, 1 or 4) in the results array. Then it will repeat this same process once again (remember, we told it to go 2 times through the iteration).
The code can be rewritten to be much simpler, using the block-form Array constructor:
def random_select(array, n)
Array.new(n) {array.sample}
end
This creates a new array of size n and fills it with random samples from the array.
Note that the above solution, like your sample code, selects from the entire array each time which allows duplicate selections. If you don't want any duplicate selections, it's even simpler, since it is the default behavior of Array#sample:
def random_select(array, n)
array.sample(n)
end

Finding the indexes of specific strings in an array, using a differently ordered equivalent array, ruby

I have two arrays: fasta_ids & frags_by_density. Both contain the same set of ≈1300 strings.
fasta_ids is ordered numerically e.g. ['frag1', 'frag2', 'frag3'...]
frags_by_density contains the same strings ordered differently e.g. ['frag14', 'frag1000'...]
The way in which frag_by_density is ordered is irrelevant to the question (but for any bioinformaticians, the 'frags' are contigs ordered by snp density).
What I want to do is find the indexes in the frag_by_density array, that contain each of the strings in fasta_ids. I want to end up with a new array of those positions (indexes), which will be in the same order as the fasta_ids array.
For example, if the order of the 'frag' strings was identical in both the fasta_ids and frags_by_density arrays, the output array would be: [0, 1, 2, 3...].
In this example, the value at index 2 of the output array (2), corresponds to the value at index 2 of fasta_ids ('frag3') - so I can deduce from this that the 'frag3' string is at index 2 in frags_by_density.
Below is the code I have come up with, at the moment it gets stuck in what I think is an infinite loop. I have annotated what each part should do:
x = 0 #the value of x will represent the position (index) in the density array
position_each_frag_id_in_d = [] #want to get positions of the values in frag_ids in frags_by_density
iteration = []
fasta_ids.each do |i|
if frags_by_density[x] == i
position_each_frag_id_in_d << x #if the value at position x matches the value at i, add it to the new array
iteration << i
else
until frags_by_density[x] == i #otherwise increment x until they do match, and add the position
x +=1
end
position_each_frag_id_in_d << x
iteration << i
end
x = iteration.length # x should be incremented, however I cannot simply do: x += 1, as x may have been incremented by the until loop
end
puts position_each_frag_id_in_d
This was quite a complex question to put into words. Hopefully there is a much easier solution, or at least someone can modify what I have started.
Update: renamed the array fasta_ids, as it is in the code (sorry if any confusion)
fasta_id = frag_id
Non optimized version. array.index(x) returns index of x in array or nil if not found. compact then removes nil elements from the array.
position_of_frag_id_in_d = frag_ids.map{|x| frag_by_density.index(x)}.compact

Replace near working array code with hash code in ruby to find mode

I was attempting to find a mode without using a hash, but now do not know if its possible, so I am wondering if someone can help me to translate my near working array code, into hash mode to make it work.
I have seen a shorter solution which I will post, but I do not quite follow it, I'm hoping this translation will help me to understand a hash better.
Here is my code, with my comments - I have bolded the part that I know will not work, as I'm comparing a frequency value, to the value of an element itself
#new = [0]
def mode(arr)
arr.each do |x| #do each element in the array
freq = arr.count(x) #set freq equal to the result of the count of each element
if freq > #new[0] && #new.include?(x) == false #if **frequency of element is greater than the frequency of the first element in #new array** and is not already there
#new.unshift(x) #send that element to the front of the array
#new.pop #and get rid of the element at the end(which was the former most frequent element)
elsif freq == #new[0] && #new.include?(x) == false #else **if frequency of element is equal to the frequency of the first element in #new array** and is not already there
#new << x #send that element to #new array
end
end
if #new.length > 1 #if #new array has multiple elements
#new.inject(:+)/#new.length.to_f #find the average of the elements
end
#new #return the final value
end
mode([2,2,6,9,9,15,15,15])
mode([2,2,2,3,3,3,4,5])
Now I have read this post:
Ruby: How to find item in array which has the most occurrences?
And looked at this code
arr = [1, 1, 1, 2, 3]
freq = arr.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
arr.sort_by { |v| freq[v] }.last
But I dont quite understand it.
What I'd like my code to do, is, as it finds the most frequent element,
to store that element as a key, and its frequency as its value.
And then I'd like to compare the next elements frequency to the frequency of the existing pair,
and if it is equal to the most frequent, store it as well,
if it is greater, replace the existing,
and if it is less than, to disregard and move to the next element.
Then of course, I'd like to return the element which has most frequencies, not the amount of frequencies,
and if two or more elements share the most frequencies, then to find the average of those numbers.
I'd love to see it with some hint of my array attempt, and maybe an explanation of that hash method that I posted above, or one that is broken down a little more simply.
This seems to fit your requirements:
def mode(array)
histogram = array.each_with_object(Hash.new(0)) do |element, histogram|
histogram[element] += 1
end
most_frequent = histogram.delete_if do |element, frequency|
frequency < histogram.values.max
end
most_frequent.keys.reduce(&:+) / most_frequent.size.to_f
end
It creates a hash of frequencies histogram, where the keys are the elements of the input array and the values are the frequency of that element in the array. Then, it removes all but the most frequent elements. Finally, it averages the remaining keys.

Resources