How do Ruby multiple code blocks work in conjunction/when chained? - ruby

Here's a function in Ruby to find if 2 unique number in an array add up to a sum:
def sum_eq_n? (arr, n)
return true if arr.empty? && n == 0
p "first part array:" + String(arr.product(arr).reject { |a,b| a == b })
puts "\n"
p "first part bool:" + String(arr.product(arr).reject { |a,b| a == b }.any?)
puts "\n"
p "second part:" + String(arr.product(arr).reject { |a,b| a + b == n } )
puts "\n"
result = arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n }
return result
end
#define inputs
l1 = [1, 2, 3, 4, 5, 5]
n = 10
#run function
print "Result is: " + String(sum_eq_n?(l1, n))
I'm confused how the calculation works to produce result. As you can see I've broken the function down into a few parts to visualize this. I've researched and understand the .reject and the .any? methods individually.
However, I'm still confused on how it fits all together in the 1 liner. How are the 2 blocks evaluated in combination? I've only found examples with .reject with 1 code block afterwards. Is .reject applied to both? I also thought there might be an implicit AND in between the 2 code blocks, but I tried to add a 3rd dummy block and it failed, so at this point I'm just not really sure how it works at all.

You can interpret the expression via these equivalent substitutions:
# orig
arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n }
# same as
pairs = arr.product(arr)
pairs.reject { |a,b| a == b }.any? { |a,b| a + b == n }
# same as
pairs = arr.product(arr)
different_pairs = pairs.reject { |a,b| a == b }
different_pairs.any? { |a,b| a + b == n }
Each block is an argument for the respective method -- one for reject, and one for any?. They are evaluated in order, and are not combined. The parts that make up the expression can be wrapped in parenthesis to show this:
((arr.product(arr)).reject { |a,b| a == b }).any? { |a,b| a + b == n }
# broken up lines:
(
(
arr.product(arr) # pairs
).reject { |a,b| a == b } # different_pairs
).any? { |a,b| a + b == n }
Blocks in Ruby Are Method Arguments
Blocks in Ruby are first-class syntax structures for passing closures as arguments to methods. If you're more familiar with object-oriented concepts than functional ones, here is an example of an object (kind of) acting as a closure:
class MultiplyPairStrategy
def perform(a, b)
a * b
end
end
def convert_using_strategy(pairs, strategy)
new_array = []
for pair in pairs do
new_array << strategy.perform(*pair)
end
new_array
end
pairs = [
[2, 3],
[5, 4],
]
multiply_pair = MultiplyPairStrategy.new
convert_using_strategy(pairs, multiply_pair) # => [6, 20]
Which is the same as:
multiply_pair = Proc.new { |a, b| a * b }
pairs.map(&multiply_pair)
Which is the same as the most idiomatic:
pairs.map { |a, b| a * b }

The return result of the first method is returned and used by the second method.
This:
result = arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n }
is functionality equivalent to:
results = arr.product(arr).reject { |a,b| a == b} # matrix of array pairs with identical values rejected
result = results.any? { |a,b| a + b == n } #true/false
This might be best visualized in pry (comments mine)
[1] pry(main)> arr = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
[2] pry(main)> n = 10
=> 10
[3] pry(main)> result_reject = arr.product(arr).reject { |a,b| a == b } # all combinations of array elements, with identical ones removed
=> [[1, 2],
[1, 3],
[1, 4],
[1, 5],
[1, 5],
[2, 1],
[2, 3],
[2, 4],
[2, 5],
[2, 5],
[3, 1],
[3, 2],
[3, 4],
[3, 5],
[3, 5],
[4, 1],
[4, 2],
[4, 3],
[4, 5],
[4, 5],
[5, 1],
[5, 2],
[5, 3],
[5, 4],
[5, 1],
[5, 2],
[5, 3],
[5, 4]]
[4] pry(main)> result_reject.any? { |a,b| a + b == n } # do any of the pairs of elements add together to equal ` n` ?
=> false
[5] pry(main)> arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n } # the one liner
=> false

Each operation "chains" into the next, which visualized looks like:
arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n }
|--|------A----->-----------B----------->-------------C----------|
Where part A, calling .product(arr), evaluates to an object. This object has a reject method that's called subsequently, and this object has an any? method that's called in turn. It's a fancy version of a.b.c.d where one call is used to generate an object for a subsequent call.
What's not apparent from that is the fact that product returns an Enumerator, which is an object that can be used to fetch the results, but is not the actual results per-se. It's more like an intent to return the results, and an ability to fetch them in a multitude of ways. These can be chained together to get the desired end product.
As a note this code can be reduced to:
arr.repeated_permutation(2).map(&:sum).include?(n)
Where the repeated_permutation method gives you all 2-digit combinations of numbers without duplicate numbers. This can be easily scaled up to N digits by changing that parameter. include? tests if the target is present.
If you're working with large arrays you may want to slightly optimize this:
arr.repeated_permutation(2).lazy.map(&:sum).include?(n)
Where that will stop on the first match found and avoid further sums. The lazy call has the effect of propagating individual values through to the end of the chain instead of each stage of the chain running to completion before forwarding to the next.
The idea of lazy is one of the interesting things about Enumerable. You can control how the values flow through those chains.

Related

A single method to delete! elements from Array by predicate and return deleted

I couldn't find a method in docs to do the following:
a = [1, 2, 3]
b = a.remove! {|x| x > 1}
puts a # [1]
puts b # [2, 3]
There is a select! method that does similar thing but it doesn't accept a predicate.
To my disappointment, delete_if, keep_if, reject! and select! mutate the array but also return the same array.
Currently, I achieve a desired in 2 steps like this but maybe there are better/smarter options?
a = [1, 2, 3]
b = a.reject {|x| x > 1}
a = a - b
puts a # [1]
puts b # [2, 3]
I don't know a way to accomplish that in 1 step, however if as in your 2nd example you'd accept to not mutate a from the ruby method, you could use Enumerable#partition
a = [1, 2, 3]
# note that b is first because first array is for elements returning true
b, a = a.partition { |x| x > 1 }
puts a # [1]
puts b # [2, 3]

Convert Ruby array of elements to Hash of counts with indices

Given a two dimensional array in Ruby:
[ [1, 1, 1],
[1, 1],
[1, 1, 1, 1],
[1, 1]
]
I'd like to create a Hash, where the keys are the counts of each internal array, and the values are arrays of indices of the original array whose internal array sizes have the particular count. The resulting Hash would be:
{ 2 => [1, 3], 3 => [0], 4 => [2] }
How do I concisely express this functionally in Ruby? I am attempting something akin to Hash.new([]).tap { |h| array.each_with_index { |a, i| h[a.length] << i } }, but the resulting Hash is empty.
There are two problems with your code. The first is that when h is empty and you write, say, h[2] << 1, since h does not have a key 2, h[2] returns the default, so this expression becomes [] << 1 #=> [1], but [1] is not attached to the hash, so no key and value are added.
You need to write h[2] = h[2] << 11. If you do that, your code returns h #=> {3=>[0, 1, 2, 3], 2=>[0, 1, 2, 3], 4=>[0, 1, 2, 3]}. Unfortunately, that's still incorrect, which takes us to the second problem with your code: you did not define the newly-created hash's default value correctly.
First note that
h[3].object_id
#=> 70113420279440
h[2].object_id
#=> 70113420279440
h[4].object_id
#=> 70113420279440
Aha, all three values are the same object! new's argument [] is returned by h[k] when h does not have a key k. The problem is that is the same array is returned for all keys k added to the hash, so you would be adding a key-value pair to an empty array for the first new key, then adding a second key-value pair to that same array for the next new key, and so on. See below for how the hash needs to be defined.
With these two changes your code works fine, but I would suggest writing it as follows.
arr = [ [1, 1, 1], [1, 1], [1, 1, 1, 1], [1, 1] ]
arr.each_with_index.with_object(Hash.new {|h,k| h[k]=[]}) { |(a,i),h|
h[a.size] << i }
#=> {3=>[0], 2=>[1, 3], 4=>[2]}
which use the form of Hash::new that uses a block to calculate the hash's default value (i.e., the value returned by h[k] when a hash h does not have a key k),
or
arr.each_with_index.with_object({}) { |(a,i),h| (h[a.size] ||= []) << i }
#=> {3=>[0], 2=>[1, 3], 4=>[2]}
both of which are effectively the following:
h = {}
arr.each_with_index do |a,i|
sz = a.size
h[sz] = [] unless h.key?(sz)
h[a.size] << i
end
h #=> {3=>[0], 2=>[1, 3], 4=>[2]}
Another way is to use Enumerable#group_by, grouping on array size, after picking up the index for each inner array.
h = arr.each_with_index.group_by { |a,i| a.size }
#=> {3=>[[[1, 1, 1], 0]],
# 2=>[[[1, 1], 1], [[1, 1], 3]],
# 4=>[[[1, 1, 1, 1], 2]]}
h.each_key { |k| h[k] = h[k].map(&:last) }
#=> {3=>[0], 2=>[1, 3], 4=>[2]}
1 The expression h[2] = h[2] << 1 uses the methods Hash#[]= and Hash#[], which is why h[2] on the left of = does not return the default value. This expression can alternatively be written h[2] ||= [] << 1.
arry = [ [1, 1, 1],
[1, 1],
[1, 1, 1, 1],
[1, 1]
]
h = {}
arry.each_with_index do |el,i|
c = el.count
h.has_key?(c) ? h[c] << i : h[c] = [i]
end
p h
This will give you
{3=>[0], 2=>[1, 3], 4=>[2]}

Find combinations in Ruby that are less than a certain number

Say I have an array [1,2,3] and I want every combination of these numbers that don't exceed 4. So I would have [1,2,3].someMethod(4) and it would give me:
[1,1,1,1]
[1,1,2]
[1,3]
[2,2]
So far I have:
(1..4).flat_map{|size| [1,2,3].repeated_combination(size).to_a }
but this gives me every possible combinations, including the ones that exceed my given limit. Is there an good way to either only get combinations that add up to my limit?
arr = [1,2,3]
(arr+[0]).repeated_combination(4).select { |a| a.reduce(:+) == 4 }.map { |a| a - [0] }
#=> [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Change == to <= if desired.
This answer, like the others, assumes arr contains natural numbers, including 1.
results = (1..4).each.with_object([]) do |size, results|
[1,2,3].repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
p results
--output:--
[[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Parameterizing the algorithm:
def do_stuff(values, target_total)
(1..target_total).each.with_object([]) do |size, results|
values.repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
end
p do_stuff([1, 2, 3], 4)
You can filter out the arrays you don't want by using the select method. Just select all the arrays that have a sum == 4 (the sum is calculated by the inject method).
all_arrs = (1..4).flat_map do |size|
[1,2,3].repeated_combination(size).to_a
end
valid_arrs = all_arrs.select do |arr|
arr.inject { |a, b| a + b } == 4
end
print valid_arrs
# Output:
# [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
A recursive approach.
def some_method(a, n)
return [[]] if n == 0
a.select { |e| e <= n }.\
flat_map { |e| some_method(a,n-e).map { |es| ([e] + es).sort } }.\
sort.\
uniq
end
p some_method([1,2,3], 4)
# => [[1, 1, 1, 1], [1, 1, 2], [1, 3], [2, 2]]
EDIT: Here is another recursive version without filtering duplicates but with opposite order. I added comments to make it clearer.
def some_method(a, n)
return [[]] if n == 0 # bottom (solution) found
return [] if a.empty? || n < 0 # no solution
max = a.max
# search all solutions with biggest value
l = some_method(a, n-max).map { |e| [max] + e }
# search all solutions without biggest value
r = some_method(a-[max],n)
l + r
end
p some_method([1,2,3], 4)
# => [[3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]

Ruby: Collect index from Array/String Matchdata

I'm new to Ruby, here's my problem : I would like to iterate through either an Array or String to obtain the index of characters that match a Regex.
Sample Array/String
a = %q(A B A A C C B D A D)
b = %w(A B A A C C B D A D)
What I need is something for variable a or b like ;
#index of A returns;
[0, 2, 3,8]
#index of B returns
[1,6]
#index of C returns
[5,6]
#etc
I've tried to be a little sly with
z = %w()
a =~ /\w/.each_with_index do |x, y|
puts z < y
end
but that didn't workout so well.
Any solutions ?
For array, you could use
b.each_index.select { |i| b[i] == 'A' }
For string, you could split it to an array first (a.split(/\s/)).
If you want to get each character's index as a hash, this would work:
b = %w(A B A A C C B D A D)
h = {}
b.each_with_index { |e, i|
h[e] ||= []
h[e] << i
}
h
#=> {"A"=>[0, 2, 3, 8], "B"=>[1, 6], "C"=>[4, 5], "D"=>[7, 9]}
Or as a "one-liner":
b.each_with_object({}).with_index { |(e, h), i| (h[e] ||= []) << i }
#=> {"A"=>[0, 2, 3, 8], "B"=>[1, 6], "C"=>[4, 5], "D"=>[7, 9]}
If you want to count occurrences of each letter you can define helper method:
def occurrences(collection)
collection = collection.split(/\s/) if collection.is_a? String
collection.uniq.inject({}) do |result, letter|
result[letter] = collection.each_index.select { |index| collection[index] == letter }
result
end
end
# And use it like this. This will return you a hash something like this:
# {"A"=>[0, 2, 3, 8], "B"=>[1, 6], "C"=>[4, 5], "D"=>[7, 9]}
occurrences(a)
occurrences(b)
This should work either for String or Array.

Result of inject is nil

I'm trying to accumulate some values if they match a condition.
Why is this snippet returning nil, when I would expect it to return 2?
[[1, 2], [2, 3], [3, 8], [4, 2]].inject(0) { |s, e| s + e[1] if e[0] <= 1}
Isn't inject the right method for this?
You should return s;
[[1, 2], [2, 3], [3, 8], [4, 2]].inject(0) { |s, e| s += e[1] if e[0] <= 1; s}
Little cleaner
[[1, 2], [2, 3], [3, 8], [4, 2]].inject(0){|s,(k,v)| s += (k<2 ? v : 0)}
You could do it in multiple steps:
>> a = [[1, 2], [2, 3], [3, 8], [4, 2]]
>> a.select { |e| e.first <= 1 }.inject(0) { |s, e| s += e.last }
=> 2
>> a.select { |e| e.first <= 1 }.map(&:last).inject(0, :+)
=> 2
Doing it all with a single inject should be more efficient but breaking it into pieces might be cleaner and the speed difference won't be noticeable unless you have really large arrays.
If you don't mind emulating a pointer with a Hash or Array, you can do it with each_with_object:
>> a.each_with_object({ :sum => 0 }) { |(k,v), m| m[:sum] += v if k <= 1 }[:sum]
=> 2
>> a.each_with_object([0]) { |(k,v), m| m[0] += v if k <= 1 }.first
=> 2
The result of your inject block is used as the value of s on the next call to your block. On the second iterator, this:
s + e[1] if e[0] <= 1
will have a value of nil because e[0] will be 2. Subsequent iterations also return nil from your block because every e[0] is larger than 1 except the first one. This is why you need to return s from your block. If you had an array like this:
[[1, 2], [2, 3], [3, 8], [4, 2], [1, 11]]
then you wouldn't even get nil out of your inject, you'd just can an exception:
NoMethodError: undefined method `+' for nil:NilClass
when your block tried to add 11 to nil.
Yet another way:
xs.map { |k, v| v if k <= 1 }.compact.inject(0, :+)
Note how Ruby suffers a bit from the lack of list-comprehensions and we have to (somewhat inefficiently) emulate it with map + compact. In a language with LC it'd look better: sum(v for (k, v) in xs if k <= 1).

Resources