Compare sequential last elements in ruby array - ruby

I have an array that contains values that I'm working on (in a specific order) something like the following:
myArray = [3,6,5,6,2,1]
I need to evaluate the elements in the array and determine the number of the elements to copy.
The rules are: I need to copy the elements where the sum of those elements is not greater than the previous element.
I can kind of express it like this:
if myArray[-3] > (myArray[-2] + myArray[-1])
elements_to_copy = [myArray[-2],myArray[-1]]
else
elements_to_copy = [myArray[-1]]
end
Which feels very crappy, as I can't work out how to make it work as an iterative function, so that I can continue up the chain till the comparison fails.
Can anyone help?

myArray = [3,5,6,2,1]
i = 0
myArray.reverse.inject do |sum, cur|
break if cur < sum
i -= 1
sum + cur
end
The range to copy is i..-1.

elements_to_copy = []
array = myArray.reverse
array.each_with_index do |item, index|
if array.values_at[0..[index-1,0].max].sum <= item
elements_to_copy << item
end
end
Should do the trick, if I understand you correctly.

Related

Merging two sorted arrays - why does this NOT work?

I am learning ruby and was given the following assignment:
given two sorted arrays like the following we must merge them into one sorted array.
array_1 = [5,8,9,11]
array_2 = [4,6,7,10]
merge(array_1, array_2)
=> [4,5,6,7,8,9,10,11]
Given this brief description, implement the merge method that takes two arrays and returns
the properly sorted array containing the items from each array.
I wrote this answer:
def merge(arr1, arr2)
i = 0
k = 0
merged_arr = []
until k = arr2.count
while arr1[i] <= arr2[k]
merged_arr.push(arr1[i])
i += 1
end
merged_arr.push(arr2[k])
k += 1
end
merged_arr
end
My instructor sent out a solution, which I understand, however I don't understand why my answer does NOT work. Can someone please explain the faulty logic? Thank you!
Here is the (correct) solution:
def merge(array_1, array_2)
i = 0
k = 0
merged_array = []
while i < array_1.count
while k < array_2.count && array_1[i] > array_2[k]
merged_array << array_2[k]
k += 1
end
merged_array << array_1[i]
i += 1
end
print merged_array.inspect
end
k = arr2.count assigns the value of arr2.count to k and evaluates to k, so until k = arr2.count is never executed.
you also need to consider the unequal length of arr1 and arr2, your instructor's solution was only right if arr1.length >= arr2.length, but if arr1.length < arr2.length, then the elements from the extra length was lost in the solution.

Problems with a sorting exercise using swapping

So I am trying to create a simple sorting program using the swapping method and I just cant get it to work properly. The code seems solid and it works up until it gets to artichoke. I've gone through and 'puts' the value of array during each iteration and pear swaps with apple, then swaps with orange, then swaps with peach and grapefruit, but when it gets to artichoke it refuses to swap like it should. It also doesn't seem like its an issue with it being the final item in the list because if I add, lets say, banana to the end it still stops at artichoke. Ultimately I want to nest the function inside an 'm.times do' function to continue the swapping until the entire list is sorted but for some reason when I put
m.times do
end
surrounding the 'n.times do' it creates another error. But I guess it doesn't even matter if I cant even get artichoke to swap with pear. Here is my code below (yes I am aware there is a .sort function, this is for learning purposes).
## Ignore this part, it is just a function I made and commented out to create
## your own list.
=begin
array = []
while true
puts "what item would you like to add to your list to be sorted?"
puts "press enter without entering an item to quit"
item = gets.chomp
break if item.empty?
array.push item
end
=end
##
array = ['pear' , 'apple' , 'orange' , 'peach' , 'grapefruit' , 'artichoke']
i = 0
m = array.length
n = m - 1
n.times do
if array.to_s[i] >= array.to_s[i+1]
swap = array[i]
array[i] = array[i+1]
array[i+1] = swap
end
i += 1
end
puts array
array = ['pear' , 'apple' , 'orange' , 'peach' , 'grapefruit' , 'artichoke']
m = array.length
n = m - 1
n.times do
i = 0
n.times do
if array[i] >= array[i+1]
temp = array[i]
array[i] = array[i+1]
array[i+1] = temp
end
i += 1
end
end
puts array
In this method of sorting, called "bubble sorting", you need nested loops. After just one run only the topmost element is guaranteed to be at its correct position. This is also known as infamous O(n*n) complexity of this poor algorithm. Btw. The inner loop can be shorther, like n-i times.
Fixed some mistakes:
array.to_s[i] is wrong because index has to be applied to array, not to_s method
... and its not necessary to call to_s since elements of this particular array already are strings
i = 0 had to be moved to the beggining of the outer loop
term "swap" refers to action, while variable is usually called "temp"
I think you mean to do array[i] >= array[i+1]
array.to_s will make a string out of your entire array and so you are comparing a character at a time of your stringified array instead of a word at a time as you seem to be intending to do.

Random number generator issues in Ruby

My intention here is just to fill up an array with numbers in order from 1, to a random number between 1 and 1000. However, after repeatedly running this code (about 50 times), the highest number I have gotten is 120, and only twice has it been over 100. The majority of my arrays were anywhere between 0 and 60. This behavior appears off to me. Am I doing something wrong?
my_array = []
i = 0
while i <= rand(1000)
my_array << i
i += 1
end
puts my_array.count
puts my_array
Your function is broken, because you're checking versus the random number. Do this:
(0..1000).collect{ rand(1000) }
This will return an array of one thousand random numbers.
Or, closer to your code:
my_array = []
i = 0
while i <= 1000
my_array << rand(1000)
i += 1
end
As per comment, what you want is:
(1..rand(1000))
(1..rand(1000)).to_a
The first results in a range, which is "easier to carry around", the second results in the populated array.
(Edit) Note:
(1..10) is inclusive - (1..10).to_a == [1,2,3,4,5,6,7,8,9,10]
(1...10) is partially exclusive - (1...10).to_a == [1,2,3,4,5,6,7,8,9] - it does not include the end of the array, but still includes the beginning.
It sounds like you want:
(1...rand(1000)).to_a
Additionally, I have amended my code to reflect what I was trying to accomplish initially. My problem was that every time I looped through my code I generated a new random number. Because of this, as 'i' incremented toward 1000 it became more and more likely that a random number would be generated that was lower than 'i'. My fix, while not as elegant as the solution above that I accepted, was to store the random number in a variable, BEFORE attempting to use it in a loop. Thanks again. Here is the amended code:
my_array = []
i = 0
g = rand(1000)
while i <= g
my_array << i
i += 1
end
puts my_array.count
puts my_array

optimize this ruby code

So this code will count the total number of pairs of numbers whose difference is K. it is naive method and I need to optimize it. suggestions?
test = $stdin.readlines
input = test[0].split(" ")
numbers = test[1].split(" ")
N = input[0]
K = input[1]
count = 0
for i in numbers
current = i.to_i
numbers.shift
for j in numbers
difference = (j.to_i - current).abs
if (difference == K)
count += 1
end
end
end
puts count
Would have been nice for you to give some examples of input and output, but I think this is correct.
require 'set'
def count_diff(numbers, difference)
set = Set.new numbers
set.inject 0 do |count, num|
set.include?(num+difference) ? count+1 : count
end
end
difference = gets.split[1].to_i
numbers = gets.split.map { |num| num.to_i }
puts count_diff(numbers, difference)
Untested, hopefully actual Ruby code
Documentation for Set: http://www.ruby-doc.org/stdlib/libdoc/set/rdoc/classes/Set.html
require 'set'
numbers_set = Set.new
npairs = 0
numbers.each do |number|
if numbers_set.include?(number + K)
npairs += 1
end
if numbers_set.include?(number - K)
npairs += 1
end
numbers_set.add(number)
end
Someone deleted his post, or his post was deleted... He had the best solution, here it is :
test = $stdin.readlines
input = test[0].split(" ")
numbers = test[1].split(" ")
K = input[1]
count = 0
numbers.combination(2){|couple| couple.inject(:-).abs == K ? count++}
puts count
You don't even need N.
I do not know Ruby so I'll just give you the big idea:
Get the list
Keep a boolean array (call it arr), marking off numbers as true if the number exists in the list
Loop through the list and see if arr[num-K] and/or arr[num+K] is true where num is a number in your list
This uses up quite a bit of memory though so another method is to do the following:
Keep a hash map from an integer n to an integer count
Go through your list, adding num+K and num-K to the hash map, incrementing count accordingly
Go through your list and see if num is in the hash map. If it is, increment your counter by count

How to count in a loop?

I'm new to Ruby, how can I count elements in a loop?
In Java I would write it like this
int[] tablica = { 23,53,23,13 };
int sum = 0;
for (int i = 0; i <= 1; i++) { // **only first two**
sum += tablica[i];
}
System.out.println(sum);
EDIT: I want only first two
You can sum all the elements in an array like this:
arr = [1,2,3,4,5,6]
arr.inject(:+)
# any operator can be here, it will be
# interpolated between the elements (if you use - for example
# you will get 1-2-3-4-5-6)
Or, if you want to iterate over the elements:
arr.each do |element|
do_something_with(element)
Or, if you need the index too:
arr.each_with_index do |element, index|
puts "#{index}: #{element}"
tablica.take(2).reduce(:+)
But seriously? What's wrong with just
tablica[0] + tablica[1]
Hey, it even works in Ruby and Java … and C, C++, Objective-C, Objective-C++, D, C#, ECMAScript, PHP, Python. Without changes.
There are many ways, but if you want the current object and a counter use the each_with_index method
some_collection.each_with_index do |o, i|
# 'o' is your object, 'i' is your index
end
EDIT: Oops, read that too quickly. You can do this
sum = 0
some_collection.each { |i| sum += i }
With Enumerable#inject:
tablica = [23, 53, 23, 13]
tablica.inject(0, :+) # 112
If you just need a sum, here is a simple way:
tablica = [ 23,53,23,13 ]
puts tablica.inject(0){|sum,current_number| sum+current_number}
For first two elements (or whatever contiguous range) you can use a range:
tablica = [ 23,53,23,13 ]
puts tablica[0..1].inject(0){|sum,current_number| sum+current_number}
What this does:
The block (the statement within {...}) is called internally by inject, once for each element in the array.
At the first iteration, sum has the initial value 0 (that we passed to inject)
And current_number contains the 0th element in the array.
We add the two values (0 and 23) and this value gets assigned to sum when the block returns.
Then on the next iteration, we get sum variable as 23 and current_number as 53. And the process repeats.

Resources