How to get each value of inject loop - ruby

I want to get each value of inject.
For example [1,2,3].inject(3){|sum, num| sum + num} returns 9, and I want to get all values of the loop.
I tryed [1,2,3].inject(3).map{|sum, num| sum + num}, but it didn't work.
The code I wrote is this, but I feel it's redundant.
a = [1,2,3]
result = []
a.inject(3) do |sum, num|
v = sum + num
result << v
v
end
p result
# => [4, 6, 9]
Is there a way to use inject and map at same time?

Using a dedicated Eumerator perfectly fits here, but I would show more generic approach for this:
[1,2,3].inject(map: [], sum: 3) do |acc, num|
acc[:map] << (acc[:sum] += num)
acc
end
#⇒ => {:map => [4, 6, 9], :sum => 9}
That way (using hash as accumulator) one might collect whatever she wants. Sidenote: better use Enumerable#each_with_object here instead of inject, because the former does not produce a new instance of an object on each subsequent iteration:
[1,2,3].each_with_object(map: [], sum: 3) do |num, acc|
acc[:map] << (acc[:sum] += num)
end

The best I could think
def partial_sums(arr, start = 0)
sum = 0
arr.each_with_object([]) do |elem, result|
sum = elem + (result.empty? ? start : sum)
result << sum
end
end
partial_sums([1,2,3], 3)

You could use an enumerator:
enum = Enumerator.new do |y|
[1, 2, 3].inject (3) do |sum, n|
y << sum + n
sum + n
end
end
enum.take([1,2,3].size) #=> [4, 6, 9]
Obviously you can wrap this up nicely in a method, but I'll leave that for you to do. Also don't think there's much wrong with your attempt, works nicely.

def doit(arr, initial_value)
arr.each_with_object([initial_value]) { |e,a| a << e+a[-1] }.drop 1
end
arr = [1,2,3]
initial_value = 4
doit(arr, initial_value)
#=> [5, 7, 10]
This lends itself to being generalized.
def gen_doit(arr, initial_value, op)
arr.each_with_object([initial_value]) { |e,a| a << a[-1].send(op,e) }.drop 1
end
gen_doit(arr, initial_value, :+) #=> [5,7,10]
gen_doit(arr, initial_value, '-') #=> [3, 1, -2]
gen_doit(arr, initial_value, :*) #=> [4, 8, 24]
gen_doit(arr, initial_value, '/') #=> [4, 2, 0]
gen_doit(arr, initial_value, :**) #=> [4, 16, 4096]
gen_doit(arr, initial_value, '%') #=> [0, 0, 0]

Related

How to improve algorithm efficiency for nested loop

Given a list of integers and a single sum value, return the first two values (from the left) that add up to form the sum.
For example, given:
sum_pairs([10, 5, 2, 3, 7, 5], 10)
[5, 5] (at indices [1, 5] of [10, 5, 2, 3, 7, 5]) add up to 10, and [3, 7] (at indices [3, 4]) add up to 10. Among them, the entire pair [3, 7] is earlier, and therefore is the correct answer.
Here is my code:
def sum_pairs(ints, s)
result = []
i = 0
while i < ints.length - 1
j = i+1
while j < ints.length
result << [ints[i],ints[j]] if ints[i] + ints[j] == s
j += 1
end
i += 1
end
puts result.to_s
result.min
end
It works, but is too inefficient, taking 12000 ms to run. The nested loop is the problem of inefficiency. How could I improve the algorithm?
Have a Set of numbers you have seen, starting empty
Look at each number in the input list
Calculate which number you would need to add to it to make up the sum
See if that number is in the set
If it is, return it, and the current element
If not, add the current element to the set, and continue the loop
When the loop ends, you are certain there is no such pair; the task does not specify, but returning nil is likely the best option
Should go superfast, as there is only a single loop. It also terminates as soon as it finds the first matching pair, so normally you wouldn't even go through every element before you get your answer.
As a style thing, using while in this way is very unRubyish. In implementing the above, I suggest you use ints.each do |int| ... end rather than while.
EDIT: As Cary Swoveland commented, for a weird reason I thought you needed indices, not the values.
require 'set'
def sum_pairs(arr, target)
s = Set.new
arr.each do |v|
return [target-v, v] if s.include?(target-v)
s << v
end
nil
end
sum_pairs [10, 5, 2, 3, 7, 5], 10
#=> [3, 7]
sum_pairs [10, 5, 2, 3, 7, 5], 99
#=> nil
I've used Set methods to speed include? lookups (and, less important, to save only unique values).
Try below, as it is much more readable.
def sum_pairs(ints, s)
ints.each_with_index.map do |ele, i|
if ele < s
rem_arr = ints.from(i + 1)
rem = s - ele
[ele, rem] if rem_arr.include?(rem)
end
end.compact.last
end
One liner (the fastest?)
ary = [10, 0, 8, 5, 2, 7, 3, 5, 5]
sum = 10
def sum_pairs(ary, sum)
ary.map.with_index { |e, i| [e, i] }.combination(2).to_a.keep_if { |a| a.first.first + a.last.first == sum }.map { |e| [e, e.max { |a, b| a.last <=> b.last }.last] }.min { |a, b| a.last <=> b.last }.first.map{ |e| e.first }
end
Yes, it's not really readable, but if you add methods step by step starting from ary.map.with_index { |e, i| [e, i] } it's easy to understand how it works.

Create a new array based on a relationship between elements in an existing array in ruby [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have the following array:
a = [1, 2, 6, 10, 11]
and I want to return a new array b that consists of the sums of adjacent elements that differ by one. In this case, the returned array would be:
b = [3, 21]
i.e. a[0] and a[1] differ by one, so sum them and add 3 to b.
a[3] and a[4] differ by one, so sum them and add 21 to b.
Update
I've made a mistake:
a = [1, 2, 6, 10, 11, 12]
It should return:
b = [3, 33]
You can initialize a b variable and use each_cons, taking two consecutive elements from the array, then use map, inside you can get the sum of those two values per array if the substraction of both values is equal to 1, as you'll get nil values then you can compact the "mapped" result:
a = [1, 2, 6, 10, 11]
b = a.each_cons(2).map do |value|
value.reduce(:+) if value[1] - value[0] == 1
end.compact
# => [3, 21]
Here's an update, you can use slice_when and convert to array the enumerator that you get as result, then to map the sum of each array element that has more than one value inside, and the compact in order to remove nil elements:
p arr.slice_when{|a, b| b != a.next}.to_a
# => [[1, 2], [6], [10, 11, 12]]
p arr.slice_when{|a, b| b != a.next}.to_a.map{|e| e.sum if e.size > 1}
# => [3, nil, 33]
p arr.slice_when{|a, b| b != a.next}.to_a.map{|e| e.sum if e.size > 1}.compact
# => [3, 33]
But this looks better using select and mapping the sum of elements at the end:
p arr.slice_when{|a, b| b != a.next}.to_a.select{|e| e.size > 1}.map(&:sum)
A benchmark:
arr = [1, 2, 6, 10, 11, 12]
Benchmark.bm do |bm|
bm.report do
iterations.times do
arr.slice_when{|a, b| b != a.next}.to_a.map{|e| e.sum if e.size > 1}.compact
end
end
bm.report do
iterations.times do
arr.slice_when{|a, b| b != a.next}.to_a.select{|e| e.size > 1}.map(&:sum)
end
end
bm.report do
iterations.times do
arr.chunk_while { |a,b| b == a.next }.select{ |a| a.size > 1 }.map{|e| e.reduce(:+)}
end
end
end
user system total real
0.920000 0.010000 0.930000 ( 0.942134)
0.920000 0.010000 0.930000 ( 0.939316)
0.940000 0.010000 0.950000 ( 0.964895)
You can use chunk_while. It 'chunks' adjacent elements if they differ by 1 (using the test #SebastiánPalma has but with abs). See Ruby documentation for more information about these methods.
a.chunk_while { |x,y| (x-y).abs == 1 }.select{ |a| a.size > 1 }.map(&:sum)
#=> [3, 21]
Note: Array#sum can only be used in Ruby >= 2.4. Use inject(&:+) otherwise:
a.chunk_while {|x,y| (x-y).abs == 1 }.select{|a| a.size > 1}.map {|a| a.inject(&:+)}
Steps
a.chunk_while {|x,y| (x-y).abs == 1 } #actually returns an enumerator.
#=> [[1, 2], [6], [10, 11]]
a.chunk_while {|x,y| (x-y).abs == 1 }.select{|a| a.size > 1}
#=> [[1, 2], [10, 11]]
a.chunk_while {|x,y| (x-y).abs == 1 }.select{|a| a.size > 1}.map(&:sum)
#=> [3, 21]
This will work with Ruby v1.9+.
arr = [1, 2, 6, 6, 10, 11, 12]
arr.drop(1).
each_with_object([[arr.first]]) { |n,a| (a.last.last - n).abs == 1 ?
a.last.push(n) : a.push([n]) }.
reject { |a| a.size == 1 }.
map(&:sum)
#=> [3, 33]
Here's a variant that allows us to skip the step reject { |a| a.size == 1 }. (I thought this might be of interest even though I don't think I'd advocate it.)
e = (arr + [Float::INFINITY]).to_enum
a = [[e.next]]
loop do
n = e.next
(a.last.last-n).abs==1 ? a.last.push(n) : (a.push([n]) if (n-e.peek).abs==1)
end
a.map(&:sum)
#=> [3, 33]
When the iterator is at the end and n #=> Float::INFINITY, e.peek raises a StopIteration exception which Kernel#loop handles by breaking out of the loop.
iterate through each element, initialize var 'sum' to elem if sum is nil. When difference between elem and next is one, add next elem to sum and store in sum, increment seq so we know there was at-least one with diff as 1.
Do this until the diff b/t elem and next is not 1, when diff is not 1 push the sum to res array if seq > 0 and reset sum to nil and seq to 0. This only takes O(n).
a.each_with_object([]).with_index do |(x, res), i|
sum ||= x
if a[i+1] && (x - a[i+1]).abs == 1
seq += 1
sum += a[i+1]
else
res << sum if seq > 0
sum = nil
seq = 0
end
end

(Ruby) First x Recursive Nums

I want to write a recursive method that returns the first num recursive numbers.
Here is my code so far:
def recursive_factorials(num)
return [1] if num == 1
arr = recursive_factorials(num-1)
arr << num * arr.last
end
Not sure what I'm doing wrong. The expected result for num = 6 is [1, 1, 2, 6, 24, 120], and I get [1, 2, 6, 24, 120, 720], so I may be close but really have no idea.
Any help would be appreciated. Also, if I am not using recursion properly please let me out.
Question is about recursion, but also you can use iteration, it's faster:
def factorials(num)
m = 1
(0...num).map {|e| e.zero? ? 1 : m *= e }
end
factorials(6)
=> [1, 1, 2, 6, 24, 120]
Or by using hash memoisation (I would say its a recursion too):
factorials = Hash.new { |h, k| h[k] = h[k-1] * k }.update(0 => 1)
factorials.values_at(*(0..5))
=> [1, 1, 2, 6, 24, 120]
Here is an example:
def recursive_factorials(num, acc = [])
acc << (num < 2 ? 1 : (num - 1) * recursive_factorials(num - 1, acc).last)
end
recursive_factorials 6
#⇒ [1, 1, 2, 6, 24, 120]
A variation of Ilya's answer:
def each_factorial
return enum_for(__method__) unless block_given?
m = 1
1.step do |i|
yield m
m *= i
end
end
each_factorial.take(6)
#=> [1, 1, 2, 6, 24, 120]

ruby** reverse array without using reverse method

This is my array and custom method to reverse an array output without using the reverse method. not sure where it broke, tried running it in console, no dice.
numbers = [1, 2, 3, 4, 5, 6]
def reversal(array)
do |item1, item2| item2 <=> item1
end
p reversal(numbers)
Here's one way to handle this. This is not very efficient but works.
def reversal(array)
reversed = []
loop do
reversed << array.pop
break if array.empty?
end
reversed
end
Here is another implementation that does the same thing:
def reversal(array)
array.each_with_index.map do |value, index|
array[array.count-index-1]
end
end
So many ways... Here are three (#1 being my preference).
numbers6 = [1, 2, 3, 4, 5, 6]
numbers5 = [1, 2, 3, 4, 5]
For all methods my_rev below,
my_rev(numbers6)
#=> [6, 5, 4, 3, 2, 1]
my_rev(numbers5)
#=> [5, 4, 3, 2, 1]
#1
def my_rev(numbers)
numbers.reverse_each.to_a
end
#2
def my_rev(numbers)
numbers.each_index.map { |i| numbers[-1-i] }
end
#3
def my_rev(numbers)
(numbers.size/2).times.with_object(numbers.dup) do |i,a|
a[i], a[-1-i] = a[-1-i] , a[i]
end
end
there are so many ways to do this
1 Conventional way
a=[1,2,3,4,5,6,7,8]
i=1
while i <= a.length/2 do
temp = a[i-1]
a[i-1] = a[a.length-i]
a[a.length-i] = temp
i+=1
end
2 Using pop
a=[1,2,3,4,5,6]
i=0
b=[]
t=a.length
while i< t do
b << a.pop
i+=1
end
3 Using pop and loop
a=[1,2,3,4,5,6]
b=[]
loop do
b << a.pop
break if a.empty?
end
a = [1,2,3,4,5]
b = []
a.length.times { |i| b << a[(i+1)*-1] }
b
=> [5,4,3,2,1]

Issue returning proper value

def sum_two(arry, sum)
p check_sums(sum, arry[0], arry[1..arry.length - 1])
end
def check_sums(target, first_num, remaining_nums)
result = []
return result if remaining_nums == []
remaining_nums.each do |n|
if first_num + n == target
result << [first_num, n]
end
end
check_sums(target, remaining_nums[0], remaining_nums[1..remaining_nums.length - 1])
end
my_arry = [2,4,6,1,3,5,7]
my_sum = 6
sum_two(my_arry, my_sum)
Above is my solution to a practice interview question. However, the output is always an empty array ([]). My question is seemingly rudimentary as I just need to return the final result array so I must be missing something obvious. Basically, I can't figure out why its printing an empty array because I feel quite confident the logic is sound.
UPDATE:
Below is an updated version of my solution in which I wrap the methods in a class and make result an instance variable so that I can maintain its state throughout the recursive call. Thanks to #BenE for mentioning that I was resetting the value every time the recursive call went through. That really cleared it up for me! Here's my new solution:
class SumTwo
#result = []
def self.sum_two(arry, sum)
p SumTwo.check_sums(sum, arry[0], arry[1..arry.length - 1])
end
def self.check_sums(target, first_num, remaining_nums)
return #result if remaining_nums == []
remaining_nums.each do |n|
if first_num + n == target
#result << [first_num, n]
end
end
check_sums(target, remaining_nums[0], remaining_nums[1..remaining_nums.length - 1])
#result
end
end
my_arry = [2,4,6,1,3,5,7]
my_sum = 6
SumTwo.sum_two(my_arry, my_sum)
The problem is that you don't return the result array that you loop on, you only return it when remaning_nums is empty, here is a working solution to you code:
def sum_two(arry, sum)
p check_sums(sum, arry[0], arry[1..arry.length - 1],[])
end
def check_sums(target, first_num, remaining_nums,result)
return result if remaining_nums == []
remaining_nums.each do |n|
if first_num + n == target
result << [first_num, n]
end
end
check_sums(target, remaining_nums[0], remaining_nums[1..remaining_nums.length - 1],result)
result
end
my_arry = [2,4,6,1,3,5,7]
my_sum = 6
sum_two(my_arry, my_sum)
If you want to return all pairs of numbers in an array whose sum is a given value, I think it's easiest to use the method Array#combination:
def sum_two(arry, sum)
arry.combination(2).select { |i,j| i+j == sum }
end
sum_two [2,4,6,1,3,5,7], 6
#=> [[2, 4], [1, 5]]
sum_two [*(1..24)], 12
#=> [[1, 11], [2, 10], [3, 9], [4, 8], [5, 7]]
sum_two [1,3, 6, 8, 2, 9, 3, 5, 7, 8, 16], 17
#=> [[1, 16], [8, 9], [9, 8]]
If you want to eliminate [8, 9] or [9, 8] in the last example, you could do this:
def sum_two(arry, sum)
arry.uniq.combination(2).select { |i,j| i+j == sum }
end
sum_two [1,3, 6, 8, 2, 9, 3, 5, 7, 8, 16], 17
#=> [[1, 16], [8, 9]]

Resources