How to get the next and previous element in an array, Ruby - ruby-on-rails

I have this array and need to replace its elements by the multiplication of its previous and next.
I do the following:
array.each_with_index{|element, index|
next_element = array[index+1]
previous_element = array[index-1]
}
array.map! {|a|
if a == array.first
a = a * next_element
elsif a == array.last
a = a * previous_element
else
a = next_element * previous_element
end
}
I expect the following result:
array = [4, 1, 6, 7, 9, 3, 0] #given array
array = [4, 24, 7, 54, 21, 0, 0] #array replaced
I get the following error:
undefined local variable or method `next_element' for Arrays:Class
Is there a easy way of getting the previous and next element of a given array element?
Am I using the array.map! method right?

This would work:
array = [4, 1, 6, 7, 9, 3, 0]
[nil, *array, nil].each_cons(3).map { |l, m, r| (l || m) * (r || m) }
#=> [4, 24, 7, 54, 21, 0, 0]
The array is surrounded by nil values, so each element has neighbors. each_cons(3) then yiels each element along with its neighbors to map which multiplies the left (l) with the right (r) neighbor, falling back to the middle element (m) if one of the neighbors happens to be nil.

You can do as following,
[array[0..1].inject(:*)] + array[0..-2].map.with_index { |x,i| x * (array[i+2] || array[i+1]) }
# => [4, 24, 7, 54, 21, 0, 0]

You defined next_element and previous_element inside the loop, so they become undefined at the end. This is a simple work around of your code, I'll assume that
you want to keep first and last element unchanged. You dont need to use map
array.each_with_index do |element, index|
if element != array.first && element != array.last
array[index] = array[index+1] * array[index-1]
end
end
array => [4, 24, 168, 1512, 4536, 0, 0]
This is not what you expected, why? Because your element, array[index] will change after each iteration you can print array after each iteration to see the result
I would suggest you to use another array to save the value of array, like this
b =[]
array.each_with_index do |element, index|
b[index] = array[index]
if element != array.first && element != array.last
b[index] = array[index+1] * array[index-1]
end
end

Seems like each_cons would be a good fit here:
[array.first] + array.each_cons(3).map { |p, _, n| p * n } + [array.last]
#=> [4, 24, 7, 54, 21, 0, 0]
This needs some more work (e.g. if the array is empty this will return [nil, nil]) but I'm sure you can figure out these edge cases.

Related

Ruby array conditionnally replacing an item in an array

I am struggling to build a small ruby snippets to compare two arrays and conditionally replace items in one of the array.
I have a "book" model which has titles and titles have chapters. I have an array with all the lines of a book and want to replace the chapters by the corresponding title in this array.
def replace_chapters_by_titles(all_lines_of_a_book)
books = Book.all
all_lines_of_a_book.each do |line|
books.each do |chapter|
if (line =~ /#{book.chapter}/)
line = "#{book.title}" #this is where I am not sure what I should do
end
end
end
end
I guess this has no impact on the array as I am just putting "#{book.title}" in line without pushing anything to the array "all_lines_of_a_book". Could someone help me find the right syntax?
You need to push to the index where line exists in array, try below code
def replace_chapters_by_titles(all_lines_of_a_book)
books = Book.all
all_lines_of_a_book.each_with_index do |line, index| # note this
books.each do |chapter|
if (line =~ /#{book.chapter}/)
all_lines_of_a_book[index] = "#{book.title}" # and this
end
end
end
all_lines_of_a_book # probably you want to return new array
end
This approach may help:
arr # => [1, 22, 5, 66, 77, 8, 88, 0]
subst # => [9, 8, 7, 6, 5, 4, 3, 2]
cond = lambda { |x| x>10 } # condition for substitution
arr.zip(arr.map(&cond)).each_with_index.map do |(a,b),i|
if b then subst[i] else a end
end # => [1, 8, 5, 6, 5, 8, 3, 0]
arr # => [1, 22, 5, 66, 77, 8, 88, 0]
subst # => [9, 8, 7, 6, 5, 4, 3, 2]
arr.each_index.map { |i| arr[i] > 10 ? subst[i] : arr[i] }
# => [1, 8, 5, 6, 5, 8, 3, 0]
or
arr.each_with_index.map { |n,i| n > 10 ? subst[i] : n }

How to optimize the solution for Two_sum code in ruby

I am woking on the solution for the following question.
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
This is the solution submitted in ruby after referring the C++ code http://leetcodeunlock.com/2016/05/20/leetcode-1-two-sum-easy/ .
def two_sum(nums, target)
hash = {}
arr = []
nums.each_with_index do |value,index|
y = target - value
if(hash.find{|key,val| key == value})
arr << hash[value]
arr << index
return arr
else
hash[y] = index
end
end
end
My submission failed with the message : Time limit exceeded. Can anyone point out the mistake and help me optimise the code?
nums = [2, 7, 11, 15]
target = 9
# this will find all combinations of 2 elements that add up to 9
results = (0...nums.size).to_a.combination(2).select { |first, last| nums[first] + nums[last] == target }
results.first #=> [0, 1]
Explanation of some parts of the code:
# Get indexes of all elements of nums array
(0...nums.size).to_a #=> [0, 1, 2, 3]
# Generate all combinations of indexes of each 2 elements
(0...nums.size).to_a.combination(2).to_a #=> [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
I have modified the line
if(hash.find{|key,val| key == value})
to
if(hash.key?(value))
to find if a specific key is present in the hash and this solved the issue.
Code
def sum_to_num(arr, num)
return [num/2, num/2] if num.even? && arr.count(num/2) > 1
a = arr.uniq.
group_by { |n| (2*n-num).abs }.
find { |_,a| a.size > 1 }
a.nil? ? nil : a.last
end
This method requires three or four passes through the array, if num is even, one to count the instances of num/2, one to remove duplicate values, one to group_by and one to find the pair of numbers that sum to the desired total. It therefore should be much faster than methods that evaluate every pair of the array's elements, particularly as the size of the array is increased.
Examples
sum_to_num [2, 11, 7, 15], 9
#=> [2, 7]
sum_to_num [2, 5, 2, 6, 1, -5, 4], 10
#=> [6, 4]
sum_to_num [2, 7, 11, -7, 15], 0
#=> [7, -7]
sum_to_num [2, 7, 11, 7, 15], 14 #???
sum_to_num [2, -7, 11, -7, 15], -14 #???
sum_to_num [2, 7, 11, 15], 17
#=> [2, 15]
sum_to_num [2, -11, 8, 15], 4
#=> [-11, 15]
sum_to_num [2, -11, 8, 15], -3
#=> [-11, 8]
sum_to_num [2, -11, 8, 15], 100
#=> nil
Explanation
Assume x and y sum to num. Then
2*x-num + 2*y-num = 2*(x+y) - 2*num
= 2*num - 2*num
= 0
meaning that 2*x-num and 2*y-num are either both zero or they have the opposite signs and the same absolute value. Similarly, if 2*x-num and 2*y-num sum to zero, then
2*x-num + 2*y-num = 0
2*(x+y) - 2*num = 0
meaning that n+m = num (which is hardly surprising considering that 2*x+num is a linear transformation.
Suppose
arr = [2, 5, 2, 6, 1, -5, 4]
num = 10
then
if num.even? && arr.count(num/2) > 1
#=> if 10.even? && arr.count(5) > 1
#=> if true && false
#=> false
Therefore, do not return [5,5].
b = arr.uniq
#=> [2, 5, 6, 1, -5, 4]
c = b.group_by { |n| (2*n-num).abs }
#=> {6=>[2], 0=>[5], 2=>[6, 4], 8=>[1], 20=>[-5]}
a = c.find { |_,a| a.size > 1 }
#=> [2, [6, 4]]
return nil if a.nil?
# do not return
a.last
#=> [6, 4]
I was doing this challenge for fun and wrote a cleaned up ruby solution.
def two_sum(nums, target)
hash = {}
nums.each_with_index { |number, index| hash[number] = index }
nums.each_with_index do |number, index|
difference = target - number
if hash[difference] && hash[difference] != index
return [index, hash[difference]]
end
end
end
# #param {Integer[]} nums
# #param {Integer} target
# #return {Integer[]}
def two_sum(nums, target)
length = nums.length
for i in 0..length
j = i+1
for a in j..length
if j < length
if nums[i] + nums[a] == target
return [i, a]
end
end
j+=1
end
end
[]
end
Well this is my way of solving this
def two_sum(nums, target)
nums.each_with_index do |value, index|
match_index = nums.find_index(target - value)
return [index, match_index] if match_index
end
nil
end
The above has the advantage that it stops execution when a match is found and so hopefully won't time out. :)

Ruby sum from nested hash

How can I return the total scores, strokes and rounds from the following array?
players = [{"Angel Cabrera"=>{"score"=>2, "strokes"=>146, "rounds"=>3}},
{"Jason Day"=>{"score"=>1, "strokes"=>145, "rounds"=>3}},
{"Bryson DeChambeau"=>{"score"=>0, "strokes"=>144, "rounds"=>3}},
{"Sergio Garcia"=>{"score"=>0, "strokes"=>144, "rounds"=>3}},
{"Ian Poulter"=>{"score"=>5, "strokes"=>162, "rounds"=>3}},
{"Vijay Singh"=>nil},
{"Jordan Spieth"=>{"score"=>-4, "strokes"=>140, "rounds"=>3}}]
I can get the strokes by doing the following but I know that isn't the best way to do it.
players.each do |x|
x.values()[0]["strokes"]
end
How can I return the sum of the strokes given the array above?
Here are three ways of doing that.
Use the form of Hash#update that employs a block to determine the values of keys that are present in both hashes being merged
players.map { |g| g.first.last }.
compact.
each_with_object({}) { |g,h| h.update(g) { |_,o,v| o+v } }
#=> {"score"=>4, "strokes"=>881, "rounds"=>18}
The steps:
a = players.map { |g| g.first.last }
#=> [{"score"=> 2, "strokes"=>146, "rounds"=>3},
# {"score"=> 1, "strokes"=>145, "rounds"=>3},
# {"score"=> 0, "strokes"=>144, "rounds"=>3},
# {"score"=> 0, "strokes"=>144, "rounds"=>3},
# {"score"=> 5, "strokes"=>162, "rounds"=>3},
# nil,
# {"score"=>-4, "strokes"=>140, "rounds"=>3}]
b = a.compact
#=> [{"score"=> 2, "strokes"=>146, "rounds"=>3},
# {"score"=> 1, "strokes"=>145, "rounds"=>3},
# {"score"=> 0, "strokes"=>144, "rounds"=>3},
# {"score"=> 0, "strokes"=>144, "rounds"=>3},
# {"score"=> 5, "strokes"=>162, "rounds"=>3},
# {"score"=>-4, "strokes"=>140, "rounds"=>3}]
b.each_with_object({}) { |g,h| h.update(g) { |_,o,v| o+v } }
#=> {"score"=>4, "strokes"=>881, "rounds"=>18}
Here, Hash#update (aka merge!) uses the block { |_,o,v| o+v }) to determine the values of keys that are present in both hashes. The first block variable (which is not used, and therefore can be represented by the local variable _) is the key, the second (o, for "old") is the value of the key in h and the third (n, for "new") is the value of the key in g.
Use a counting hash
players.map { |g| g.first.last }.
compact.
each_with_object(Hash.new(0)) { |g,h| g.keys.each { |k| h[k] += g[k] } }
Hash.new(0) creates an empty hash with a default value of zero, represented by the block variable g. This means that if a hash h does not have a key k, h[k] returns the default value (but does not alter the hash). h[k] += g[k] above expands to:
h[k] = h[k] + g[k]
If h does not have a key k, h[k] on the right side is therefore replaced by 0.
Sum values and then convert to a hash
If you are using Ruby v1.9+ and the keys are guaranteed to have the same order in each hash, a third way it could be done is as follows:
["scores", "strokes", "rounds"].zip(
players.map { |g| g.first.last }.
compact.
map(&:values).
transpose.
map { |arr| arr.reduce(:+) }
).to_h
#=> {"scores"=>4, "strokes"=>881, "rounds"=>18}
The steps (starting from b above) are:
c = b.map(&:values)
#=> [[ 2, 146, 3],
# [ 1, 145, 3],
# [ 0, 144, 3],
# [ 0, 144, 3],
# [ 5, 162, 3],
# [-4, 140, 3]]
d = c.transpose
#=> [[ 2, 1, 0, 0, 5, -4],
# [146, 145, 144, 144, 162, 140],
# [ 3, 3, 3, 3, 3, 3]]
totals = d.map { |arr| arr.reduce(:+) }
#=> [4, 881, 18]
e = ["scores", "strokes", "rounds"].zip(totals)
#=> [["scores", 4], ["strokes", 881], ["rounds", 18]]
e.to_h
#=> {"scores"=>4, "strokes"=>881, "rounds"=>18}
Use this code:
#total= 0
players.each do |x|
a= x.values[0]
if a.class == Hash
#total += a["strokes"]
end
end
puts #total

How to compare more than 3 array to find the one with highest priority

I have following array set holding priorities,
array1 = [1, 2, 1, 2, 3]
array2 = [1, 1, 1, 2, 3]
|
|
|
arrayn = [1, 3, 1, 2, 3]
I want to find out easiest way to find out the array variable which having highest priority i.e 1
It should check in a following way,
If last element of any array is lowest that should be return.
If last element of more than one array is same then previous element of that array should be matched
In above given example last 3 values of all the 3 array are same so it will check for the 2nd last element
array1 = 2
array2 = 1
array3 = 3
So it should return array2.
Please let me know if any one fail to understand the question
More Examples
Example 1
arr1 = [1,1,1,1,3]
arr2 = [3,3,3,3,2]
should return arr2
Example 2
arr1 = [2,2,2,1,3]
arr2 = [1,2,1,3,3]
should return arr1, as arr1.last == arr2.last (i.e. 3)
but arr1[3] < arr2[3] i.e. (1 < 3)
For equal sized arrays (and if I understand the question right) this should work:
arrays = [
[1, 2, 1, 1, 3],
[1, 3, 1, 2, 3],
[1, 1, 1, 2, 3],
[1, 3, 1, 1, 3]
]
p arrays[arrays.transpose.reverse.map{|el|
next if el.count(el.min) != 1
el.rindex(el.min)
}.compact[0]]
Result:
#=> [1, 1, 1, 2, 3]
Step by step: Transpose arrays and start to check from last chunk. If there is not one minimal priority skip this chunk (put nil in output), if only one - get it's index. Remove all nil elements and use first found index to print desired array.
UPDATED
If you wish to handle the case when initial array of arrays doesn't have answer you could change the code in this way:
idx = arrays.transpose.reverse.map{|el|
next if el.count(el.min) != 1
el.rindex(el.min)
}.compact[0]
p idx ? arrays[idx] : "no answer"
The result for [[1,1,1,1,1], [1,1,1,1,2], [2,1,1,1,1], [2,1,1,1,2]] will be:
#=> "no answer"
tab = [array1, array2, array3, arrayn]
i = -1
while tab.count != 1 && tab.count + i >= 0
tab = tab.select { |t| t[i] == tab.map { |j| j[i] }.min }
i = i - 1
end
result = tab[0]

How to sort a ruby array in ascending order but keep zero last

I am trying to sort a Ruby array with the following function
#prices = #item.prices.sort { |x,y| x.total <=> y.total }
Which orders from the lowest to the highest cost. However some products have a total of 0.00 and I want them to appear last rather than at the top.
I have tried a few things but would like some way to modify this block to sort zero at the bottom but keep the rest in ascending order.
Thanks.
Try this out, I think it is doing what you request:
#prices = #item.prices.sort {|a,b| a.total==0 ? 1 : b.total==0 ? -1 : a.total<=>b.total}
Just for the record:
>> a = [0, 1, 3, 0, 2, 5, 0, 9]
=> [0, 1, 3, 0, 2, 5, 0, 9]
>> a.sort_by { |x| x.zero? ? Float::MAX : x }
=> [1, 2, 3, 5, 9, 0, 0, 0]
On most platforms 1.0/0 will evaluate to Infinity, so you can also use this instead of Float::MAX:
>> b = [1,4,2,0,5,0]
=> [1, 4, 2, 0, 5, 0]
>> Inf = 1.0/0
=> Infinity
>> b.sort_by { |x| x.zero? ? Inf : x }
=> [1, 2, 4, 5, 0, 0]
prices = [0, 1, 2, 0,4, 3]
prices = prices.sort_by do |price|
[
if price == 0
1
else
0
end,
price
]
end
p prices
# => [1, 2, 3, 4, 0, 0]
The trick here is that arrays are compared by comparing their first elements, but if those elements are equal, then by comparing their next elements, and so on. So having the sort_by block yield an array lets you determine primary sort order, secondary sort order, and so on in a clean manner.
So devise a comparator to do that ...
if x.total == 0
# always consider 0 "largest" and no 0 can be larger than another
# (make sure 0.0 is 0 and not a number really close to 0)
# perhaps x or y should be first for other reasons as well?
1
else
# otherwise lower to higher as normal
x.total <=> y.total
end
Or without comments:
foo.sort {|x, y| if x.total == 0 then 1 else x.total <=> y.total end}
Happy coding.
This would feel less hacky and less write-only to me:
prices = prices.sort_by do |price|
zero_status = price.zero? ? 1 : 0
[zero_status, price]
end
because it's an idiomatic way of sorting something by two criteria, which is what you're doing here.

Resources