Is there a nicer way to calculate lag (value - previous value) in ruby? This is what I came up with. If the lag is greater than 0 I return 1, if it is less I return -1
(my code below does not work. Using ruby 2.3.5)
def lagChange(arr,k=1)
prev=nil
ret=[]
arr.each{|x|
if(x.to_i>-9999)
ret<<if(x-prev>0){1}else{-1}end
else
ret<<"NA"
end
prev=x.to_i
}
return ret
end
lagChange([1,2,3,5]) #NA 1 1 -1
if(x-prev>0){1}else{-1}end is not valid Ruby.
def lagChange(arr,k=1)
prev = nil
ret = []
arr.each{|x|
if prev == nil
ret << "NA"
elsif(x.to_i>-9999)
ret << (x-prev > 0 ? 1 : -1)
end
prev=x.to_i
}
return ret
end
lagChange([1,2,3,1])
=> ["NA", 1, 1, -1]
A shorter version could be
def lag_change(arr)
arr.each_cons(2).map {|x,y| y-x > 0 ? 1 : -1}.unshift('NA')
end
> lag_change([1,2,3,1])
=> ["NA", 1, 1, -1]
Looks like the spaceship operator might come in handy here:
def lagChange(arr)
arr.map.with_index do |x, idx|
idx == 0 ? "NA" : x <=> arr[idx - 1]
end
end
lagChange([1,2,2,1])
=> ["NA", 1, 0, -1]
Note that this also returns 0 if they are equal(no lag).
Related
I am trying to solve some problems in ruby get a hold.
I am trying compare two arrays with each other.
Array1 = [1,0,1,0,1,1]
Array2= [0,0,1,0,1,0]
I am getting this input from the user. Then I am comparing the votes. If both persons have upvoted 1 at same index, I am trying increment an empty array by 1.
def count_votes
bob_votes = gets.chomp
alice_votes = gets.chomp
bvotes = bob_votes.split('')
avotes = alice_votes.split('')
common_upvotes = []
bvotes.each.with_index(0) do |el, i|
if bvotes[i] == 1
common_upvotes << 1
end
end
I actually want to compare avotes with bvotes and then increment empty array by 1. I need a little help
You can use Array#zip and Enumerable#count:
array1 = [1,0,1,0,1,1]
array2= [0,0,1,0,1,0]
array1.zip(array2)
#⇒ [[1, 0], [0, 0], [1, 1], [0, 0], [1, 1], [1, 0]]
array1.zip(array2).count { |v1, v2| v1 == v2 && v1 == 1 }
#⇒ 2
or (credits to #engineersmnky):
array1.zip(array2).count { |v1, v2| v1 & v2 == 1 }
or even better (credits to #Stefan):
array1.zip(array2).count { |values| values.all?(1) }
or
array1.
zip(array2).
reject { |v1, v2| v1 == 0 || v2 == 0 }.
count
#⇒ 2
Sidenote: capitalized Array1 declares a constant. To declare a variable, use array1 instead.
The number of indices i for which Array1[i] == 1 && Array2[i] == 1 is
Array1.each_index.count { |i| Array1[i] == 1 && Array2[i] == 1 }
#=> 2
The array of indices i for which Array1[i] == 1 && Array2[i] == 1 is
Array1.each_index.select { |i| Array1[i] == 1 && Array2[i] == 1 }
#=> [2, 4]
The number of indices i for which Array1[i] == Array2[i] is
Array1.each_index.count { |i| Array1[i] == Array2[i] }
#=> 4
If you want to build a new array tracking the index where there is a match of upvotes:
a1 = [1,0,1,0,1,1]
a2= [0,0,1,0,1,0]
p [a1, a2].transpose.map {|x| x.reduce(:&)}
#=> [0, 0, 1, 0, 1, 0]
For just counting, this is another way:
a1 = [1,0,1,0,1,1]
a2= [0,0,1,0,1,0]
votes = 0
a1.each_with_index do |a1, idx|
votes +=1 if (a1 + a2[idx]) == 2
end
p votes #=> 2
In one line:
a1.each_with_index { |a1, idx| votes += 1 if (a1 + a2[idx]) == 2 }
Trying to multiply each number by array position, and it's coming out false:
def the_sum(number)
i = 0
number = 0
ans = 0
while i < 0
ans = string[idx] * string.index
i += idx
end
return ans
end
test =
the_sum([2, 3]) == 3 # (2*0) + (3*1)
the_sum([2, 3, 5]) == 13 # (2*0) + (3*1) + (5*2)
and it comes out false?
There are a few problems here
def the_sum(number)
i = 0
number = 0 # You just cleared your input variable!
ans = 0
while i < 0 # You previously i = 0 so this will never be true
ans = string[idx] * string.index
i += idx
end
return ans # Ans is and was always 0
end
This can be fixed by calling each_with_index on the Array that you're passing.
def the_array_sum(array)
ans = 0
array.each_with_index do |val, index|
ans += val * index
end
return ans
end
the_array_sum([2, 3]) == 3
# => true
the_array_sum([2, 3, 5]) == 13
# => true
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 often find myself checking multiple conditions. How can I cut down on the number of lines used to achieve the same effect?
def super_fizzbuzz(array)
final = []
for num in array
if num % 15 == 0
final << 'FizzBuzz'
elsif num % 5 == 0
final << 'Buzz'
elsif num % 3 == 0
final << 'Fizz'
else
final << num
end
end
final
end
def super_fizzbuzz(array)
array.map do |num|
a = []
a << 'Fizz' if num % 3 == 0
a << 'Buzz' if num % 5 == 0
a.empty? ? num : a.join()
end
end
def super_fizzbuzz(array)
final = []
array.each do |num|
num % 15 == 0 ? final << 'FizzBuzz' : num % 5 == 0 ? final << 'Buzz' : num % 3 == 0 ? final << 'Fizz' : final << num
end
final
end
But your way is more readable.
def super_fizzbuzz(array)
array.map do |num|
case 0
when num % 15 then "FizzBuzz"
when num % 5 then "Buzz"
when num % 3 then "Fizz"
else num
end
end
end
This is slightly more complex, but reduces number of explicit coded conditionals to 2:
FIZZBUZZ = { 3 => 'Fizz', 5 => 'Buzz' }
def super_fizzbuzz(array)
array.map do |num|
fbs = FIZZBUZZ.select do |divisor,cat|
num % divisor == 0
end.values
fbs.empty? ? num : fbs.join
end
end
There is always the danger when coding for DRY that you take things too far. In this case, with only two overlapping categories, I think the above is a little unwieldy. However, add another category or two:
FIZZBUZZ = { 3 => 'Fizz', 5 => 'Buzz', 7 => 'Boom', 11 => 'Whizz' }
and it starts to look smarter.
Quote:
I think Fizz-Buzz is "hard" for some programmers because (#1) it doesn't fit into any of the patterns that were given to them in school assignments, and (#2) it isn't possible to directly and simply represent the necessary tests, without duplication, in just about any commonly-used modern programming language.
Source: c2.com Wiki
Another way:
def super_fizzbuzz(arr)
arr.map do |e|
s = ''
s << 'Fizz' if (e%3).zero?
s << 'Buzz' if (e%5).zero?
s = e if s.empty?
s
end
end
super_fizzbuzz [9, 25, 225, 31]
#=> ["Fizz", "Buzz", "FizzBuzz", 31]
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().