Related
I have a unique sorted array: [2,4,6,8,10].
I have a variable called i. If i is 5, I want to return the elements in the array that 5 falls between. In this case [4,6]. If i is 8, then [8,10].
How should I go about this?
I've tried with partition, to some extent. If i happens to be a number directly equal to one of the values in the array. This seems to work:
a=[2,4,6,8,10]
i = 6
a.partition { |v| v < i }.max[0..1] # returns [6,8]
However, if i is a number not directly equal to any of the values in the array. For example 5, it gets a little trickier.
I got it working for the last case:
a=[2,4,6,8,10]
i = 5
partition = a.partition { |v| v < i }
[].tap { |a| a << partition[0].max; a << partition[1].min } # returns [6,8]
While this works, I am looking to see if there is a better way to write this logic.
You could use Enumerable#each_cons.
def mind_the_gap(arr, n)
arr.each_cons(2).find { |l,u| l <= n && n < u }
end
arr = [2,4,6,8,10]
mind_the_gap(arr, 5) #=> [4,6]
mind_the_gap(arr, 8) #=> [8,10]
mind_the_gap(arr, 1) #=> nil
mind_the_gap(arr, 10) #=> nil
If you don't want the last two examples to return nil, you could change the method as follows.
def mind_the_gap(arr, n)
rv = arr.each_cons(2).find { |l,u| l <= n && n < u }
return rv unless rv.nil?
n < arr.first ? :low : :high
end
mind_the_gap(arr, 5) #=> [4,6]
mind_the_gap(arr, 8) #=> [8,10]
mind_the_gap(arr, 1) #=> :low
mind_the_gap(arr, 10) #=> :high
Another way is to use Enumerable#slice_when.
def mind_the_gap(arr, n)
a = arr.slice_when { |l,u| l <= n && n < u }.to_a
return [a.first.last, a.last.first] unless a.size == 1
n < arr.first ? :low : :high
end
mind_the_gap(arr, 5) #=> [4,6]
mind_the_gap(arr, 8) #=> [8,10]
mind_the_gap(arr, 1) #=> :low
mind_the_gap(arr, 10) #=> :high
If you're looking for elements inside a sorted array, the "better way" probably involves bsearch or bsearch_index.
The second element in the pair is the first element in the array that is greater than your variable, so bsearch_index can return it directly. You need to check it isn't nil or 0 before returning the found element and the previous one :
a = [2, 4, 6, 8, 10]
def find_surrounding_pair(array, element)
second_index = array.bsearch_index { |x| x > element }
array[second_index - 1, 2] if second_index && second_index > 0
end
puts find_surrounding_pair(a, 1).nil?
puts find_surrounding_pair(a, 2) == [2, 4]
puts find_surrounding_pair(a, 7) == [6, 8]
puts find_surrounding_pair(a, 8) == [8, 10]
puts find_surrounding_pair(a, 12).nil?
#=> true * 5
The complexity of this method should be O(log n).
what about this
val = 5
a = [2,4,6,8,10] # assuming it's sorted
a.slice(a.rindex {|e| e <= val}, 2)
It doesn't account for the case when the lookup value is equal or bigger the last element of the array. I'd probably append a nil element for this, if that would be appropriate for the problem.
This looks like a good use to check for the inclusion in a range:
a = [2,4,6,8,10]
b = 5
a.each_cons(2).select { |i, j| (i .. j) === b }
# => [[4, 6]]
It's not clear exactly what you mean by "falls between". In the code above 8 would fall between two sets of numbers:
b = 8
a.each_cons(2).select { |i, j| (i .. j) === b }
# => [[6, 8], [8, 10]]
if the test is i <= b <= j. If it's i <= b < j then use ... instead of ..:
a.each_cons(2).select { |i, j| (i ... j) === b }
# => [[8, 10]]
I'm not a big fan of using ... but it simplifies the code.
From the Range documentation:
Ranges constructed using .. run from the beginning to the end inclusively. Those created using ... exclude the end value.
You could change that to:
a.each_cons(2).select { |i, j| i <= b && b <= j }
or:
a.each_cons(2).select { |i, j| i <= b && b < j }
if those work better for your mind. Using a Range is a little slower, but not radically so.
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.
I would like to write a program in ruby 1.9.3 ver. which collects unique value ranges and then calculates amount of numbers in these ranges.
For example lets use 3 ranges (1..3), (6..8) and (2..4). It will return array with two ranges (1..4), (6..8) and amount of numbers - 7.
I wrote the following code:
z= []
def value_ranges(start, finish, z)
range = (start..finish)
arr = z
point = nil
if arr.empty?
point = nil
else
arr.each { |uniq|
if overlap?(uniq,range) == true
point = arr.index(uniq)
break
else
point = nil
end
}
end
if point != nil
if arr[point].first >= start && arr[point].end <= finish
range = (start..finish)
elsif arr[point].first >= start
range = (start..arr[point].end)
elsif arr[point].end <= finish
range = (arr[point].first..finish)
else
range = (arr[point].first..arr[point].end)
end
arr[point] = range
else
arr << range
end
print arr
end
def overlap?(x,y)
(x.first - y.end) * (y.first - x.end) >= 0
end
Problem comes when program meets a range which overlaps two already collected ranges.
For example (1..5) (7..9) (11..19) and the next given range is (8..11).
It should link both ranges and return the following result - (1..5),(7..19).
I don't have an idea how to recheck whole array without creating infinite loop. Also what is the best way to calculate amount of numbers in ranges?
Here are two Ruby-like ways of doing it.
arr = [1..3, 6..8, 2..4]
#1 Efficient approach
First calculate the amalgamated ranges:
a = arr[1..-1].sort_by(&:first).each_with_object([arr.first]) do |r,ar|
if r.first <= ar.last.last
ar[-1] = ar.last.first..[ar.last.last,r.last].max
else
ar << r
end
end
#=> [1..4, 6..8]
Then compute the total number of elements in those ranges:
a.reduce(0) { |tot,r| tot + r.size }
#=> [1..4, 6..8].reduce(0) { |tot,r| tot + r.size }
#=> 7
Explanation
b = arr[1..-1]
#=> [6..8, 2..4]
c = b.sort_by(&:first)
#=> [2..4, 6..8]
enum = c.each_with_object([1..3])
#=> #<Enumerator: [2..4, 6..8]:each_with_object([1..3])>
The contents of the enumerator enum will be passed into the block and assigned to the block variables by Enumerator#each, which will call Array#each. We can see the contents of the enumerator by converting it to an array:
enum.to_a
#=> [[2..4, [1..3]], [6..8, [1..3]]]
and we can use Enumerator#next to step through the enumerator. The first element of the enumerator passed to the block by each is [2..4, [1..3]]. This is assigned to the block variables as follows:
r, ar = enum.next
#=> [2..4, [1..3]]
r #=> 2..4
ar #=> [1..3]
We now perform the block calculation
if r.first <= ar.last.last
#=> 2 <= (1..3).last
#=> 2 <= 3
#=> true
ar[-1] = ar.last.first..[ar.last.last,r.last].max
#=> ar[-1] = 1..[3,4].max
#=> ar[-1] = 1..4
#=> 1..4
else # not executed this time
ar << r
end
This is not so mysterious. So I don't have to keep saying "the last range of ar", let me define:
ar_last = ar.last
#=> 1..3
First of all, because we began by sorting the ranges by the beginning of each range, we know that when each element of enum is passed into the block:
ar_last.first <= r.first
For each element of enum passed into the block for which:
r.first <= ar_last.last
we compare r.last with ar_last.last. There are two possibilities to consider:
r.last <= ar_last.last, in which case the two ranges overlap and therefore ar_last would not change; and
r.last > ar_last.last, in which case the upper end of ar_last must be increased to r.last.
Here,
2 = r.first <= ar_last.last = 3
4 = r.last > ar_last.last = 3
so ar_last is changed from 1..3 to 1..4.
each now passes the last element of enum into the block:
r, ar = enum.next
#=> [6..8, [1..4]]
r #=> 6..8
ar #=> [1..4]
if r.first <= ar.last.last
#=> (6 <= 4) => false this time
...
else # executed this time
ar << r
#=> ar << (6..8)
#=> [1..4, 6..8]
end
and
a = ar #=> [1..4, 6..8]
This time, r.first > ar_last.last, meaning the range r does not overlap ar_last, so we append r to ar, and ar_last now equals r.
Lastly:
a.reduce(0) { |tot,r| tot + r.size }
#=> [1..4, 6..8].reduce(0) { |tot,r| tot + r.size }
#=> 7
which we could alternatively write:
a.map(&:size).reduce(:+)
#2 Easy but inefficient
Here is an easy, but not especially efficient, method that uses Enumerable#slice_when, newly-minted in v2.2.
arr = [(1..3), (6..8), (2..4)]
To calculate the amagamated ranges:
a = arr.flat_map(&:to_a)
.uniq
.sort
.slice_when { |i,j| i+1 != j }
.map { |ar| (ar.first..ar.last) }
#=> [1..4, 6..8]
The total number of elements in those ranges is calculated as in #1
Explanation
Here are the steps:
b = arr.flat_map(&:to_a)
#=> [1, 2, 3, 6, 7, 8, 2, 3, 4]
c = b.uniq
#=> [1, 2, 3, 6, 7, 8, 4]
d = c.sort
#=> [1, 2, 3, 4, 6, 7, 8]
e = d.slice_when { |i,j| i+1 != j }
#=> #<Enumerator: #<Enumerator::Generator:0x007f81629584f0>:each>
a = e.map { |ar| (ar.first..ar.last) }
#=> [1..4, 6..8]
We can see the contents of the enumerator e by converting it to an array:
e.to_a
#=> [[1, 2, 3, 4], [6, 7, 8]]
I've been implementing an exercise on bubbling sorting.
So far, managed to bubble sort through an array.
Let's say, we have our array = ([5,6,7], [2,3,4])
How can I bubble sort through this without modifying the original array or using sort?
The exercise requires that I do not copy/duplicate/clone/edit or use a sort method.
My code to bubble sort through a regular array:
def bubble_sort(array)
is_sorted = false
until is_sorted
is_sorted = true
(array.count - 1).times do |i|
if array[i] > array[i + 1]
array[i], array[i + 1] = array[i + 1], array[i]
is_sorted = false
end
end
end
end
arr
As usual I learned quite a lot solving the OPs homework :-)
Firstly "do not copy/duplicate/clone/edit or use a sort method" just means that you have to implement your own algorithm. It does not mean you shouldn't copy the array passed to your function. You must copy your original array to avoid the side effect of modifying the original array.
However! if you simply copy the array new_array = array ruby will copy by reference.
That means that both variables will still point to the same data structure. Your sort will happily sort the numbers and the array passed as a parameter will be modified.
To fix this you need to copy the array using the dup function.
new_array = array.dup
Then the bubble sort function ends up looking like this:
def bubble_sort(array)
result = array.dup
begin
sorted = true
(0..result.length - 2).each do |i|
if result[i] > result[i + 1]
result[i], result[i+1] = result[i+1], result[i]
sorted = false
end
end
end while !sorted
result
end
Some test code:
a = [9,8,7,6,5,5,9,]
puts "array before sorting:"
puts a.inspect
puts "sorted array returned by function"
puts bubble_sort(a).inspect
puts "original array after sorting:"
puts a.inspect
Resulting output:
>ruby mysort.rb
array before sorting:
[9, 8, 7, 6, 5, 5, 9]
sorted array returned by function
[5, 5, 6, 7, 8, 9, 9]
original array after sorting:
[9, 8, 7, 6, 5, 5, 9]
Create a new array that is a copy of the original and sort that. That will let you keep the original array and have the sorted array.
Can you just combine the two arrays into a single array and then run a typical bubble sort?
def combine_arrays(arr1,arr2)
final = arr1 + arr2
sorted = true
while sorted do
sorted = false
(0..final.length - 2).each do |x|
if final[x] > final[x+1]
final[x], final[x+1] = final[x+1], final[x]
sorted = true
end
end
end
final
end
p combine_arrays([1,3,5],[2,4,6]) => [1, 2, 3, 4, 5, 6]
i think the simplest way for Bubble Sorting multi Dimensional Arrays is:
int main()
{
const size_t row = 4,col=6;
int temp;
int arr[row][col] = { { 6, 5, 4, 3, 2, 1 }, { 12, 11, 10, 9, 8, 7 }, {18, 17, 16, 15, 14, 13 }, { 24,23,22,21,20,1 } };
for (auto &row : arr)
{
for (int i = 0; i != col; ++i)
{
int flag = 0;
for (int j = 0; j != col-i - 1; ++j)
{
if (row[j]>row[j+1])
{
temp = row[j];
row[j] = row[j + 1];
row[j + 1] = temp;
flag = 1;
}
}
if (!flag)break;
}
}
for (auto &row : arr)
{
for (auto col : row)
cout << col << " ";
cout << endl;
}
return 0;
}
I have 2 lists that have dates and data. Each list is in the proper order as noted by the sequence number. Now I need to merge the 2 lists together and keep everything in the correct order.
For example:
List A
20101001 A data 1 seq1
20101001 A data 2 seq2
20101005 A data 3 seq3
List B
20101001 B data 1 seq1
20101003 B data 2 seq2
etc...
I need the new list to look like this:
20101001 A data 1 seq1
20101001 A data 2 seq2
20101001 B data 1 seq3
20101003 B data 2 seq4
20101005 A data 3 seq5
2 things that I thought of is merging the lists together and applying the sequence number prior to inserting them into a db or I can insert them into the db with the current sequence and pull them back out again to merge them together, but that seems like an extra step and kludgy.
Any ideas on the best way to go about this?
Assuming your lists are in Ruby Arrays, and the objects in the lists have attributes defined (such as obj.sequence_number), one way to merge and sort the lists would be:
First merge the lists as a union:
#merged_list = #list_a | #list_b
Then sort the merged_list with the appropriate sorting rule:
#merged_list.sort! {|a, b| a.date <=> b.date # or whatever your sorting rule is... }
Edit:
Once the merged array is sorted, you can re-define the sequence_number:
#merged_list.each_with_index {|obj, index| obj.sequence_number = "seq#{index+1}"}
Edit:
Same thing applies if your objects in the lists are themselves just simple arrays:
#merged_list.sort! {|a, b| a[0] <=> b[0] # or whatever your sorting rule is... }
#merged_list.each_with_index {|obj, index| obj[2] = "seq#{index+1}"}
Try this:
(listA + listB).sort!{|a, b| a.sequence_no <=> b.sequence_no}
This is an algorithm for merging an arbitrary number of sorted lists in more or less linear time:
def merge_sorted(*lists)
# the lists will be modified, so make (shallow) copies
lists = lists.map(&:dup)
result = []
loop do
# ignore lists that have been exhausted
lists = lists.reject(&:empty?)
# we're done if all lists have been exhausted
break if lists.empty?
# find the list with the smallest first element
top = lists.inject do |candidate, other|
candidate.first < other.first ? candidate : other
end
result << top.shift
end
result
end
list1 = [1, 2, 5, 6, 9]
list2 = [2, 3, 4, 11, 13]
list3 = [1, 2, 2, 2, 3]
p merge_sorted(list1, list2, list3)
# => [1, 1, 2, 2, 2, 2, 2, 3, 3, 4, 5, 6, 9, 11, 13]
For each iteration it finds the list with the smallest first element, and shifts this element off of it onto the results list. It does this until all lists are empty.
I say more or less linear time since it's actually O(n × m) where n is the number of lists and m is the total number of elements in the lists but I think this can safely be simplified to O(m) for most cases since n will be small in comparison to m.
This uses with_index which is a nice way to add an index value to an iterator:
result = (list_a + list_b).sort_by { |a| a[0 .. -2] }.map.with_index { |a, i| a[0 .. -2] + (1 + i).to_s }
puts result
# >> 20101001 A data 1 seq1
# >> 20101001 A data 2 seq2
# >> 20101001 B data 1 seq3
# >> 20101003 B data 2 seq4
# >> 20101005 A data 3 seq5
Here's some variations with benchmarks:
require 'benchmark'
list_a = [
'20101001 A data 1 seq1',
'20101001 A data 2 seq2',
'20101005 A data 3 seq3'
]
list_b = [
'20101001 B data 1 seq1',
'20101003 B data 2 seq2'
]
# #1
result = (list_a + list_b).sort_by { |a| a[0 .. -2] }.map.with_index { |a, i| a[0 .. -2] + (1 + i).to_s }
result # => ["20101001 A data 1 seq1", "20101001 A data 2 seq2", "20101001 B data 1 seq3", "20101003 B data 2 seq4", "20101005 A data 3 seq5"]
# #2
result = (list_a + list_b).map{ |r| r[0 .. -2] }.sort.map.with_index { |a, i| a + (1 + i).to_s }
result # => ["20101001 A data 1 seq1", "20101001 A data 2 seq2", "20101001 B data 1 seq3", "20101003 B data 2 seq4", "20101005 A data 3 seq5"]
# #3
i = 0
result = (list_a + list_b).map{ |r| r[0 .. -2] }.sort.map { |a| i += 1; a + i.to_s }
result # => ["20101001 A data 1 seq1", "20101001 A data 2 seq2", "20101001 B data 1 seq3", "20101003 B data 2 seq4", "20101005 A data 3 seq5"]
# #4
i = 0; result = (list_a + list_b).sort.map { |a| i += 1; a[-1] = i.to_s; a }
result # => ["20101001 A data 1 seq1", "20101001 A data 2 seq2", "20101001 B data 1 seq3", "20101003 B data 2 seq4", "20101005 A data 3 seq5"]
n = 75000
Benchmark.bm(7) do |x|
x.report('#1') { n.times { (list_a + list_b).sort_by { |a| a[0 .. -2] }.map.with_index { |a, i| a[0 .. -2] + (1 + i).to_s } } }
x.report('#2') { n.times { (list_a + list_b).map{ |r| r[0 .. -2] }.sort.map.with_index { |a, i| a + (1 + i).to_s } } }
x.report('#3') { n.times { i = 0; (list_a + list_b).map{ |r| r[0 .. -2] }.sort.map { |a| i += 1; a + i.to_s } } }
x.report('#4') { n.times { i = 0; (list_a + list_b).sort.map { |a| i += 1; a[-1] = i.to_s } } }
end
# >> user system total real
# >> #1 1.150000 0.000000 1.150000 ( 1.147090)
# >> #2 0.880000 0.000000 0.880000 ( 0.880038)
# >> #3 0.720000 0.000000 0.720000 ( 0.727135)
# >> #4 0.580000 0.000000 0.580000 ( 0.572688)
It's good to benchmark.