Operator Array#<< failed in shorthand form of reduce - arrays

There is well-known shorthand form to pass block to any method, based on Symbol#to_proc implementation.
Instead of:
[1,2,3].reduce(0) { |memo, e| memo + e }
# or
[1,2,3].reduce { |memo, e| memo.+(e) }
one might write:
[1,2,3].reduce &:+
The above is an exact “synonym” of the latter “standard notation.”
Let us now have two arrays:
a = [[1,"a"],[2,"b"]]
b = [[3,"c"],[4,"d"]]
While both
b.reduce(a) { |memo, e| memo << e }
# and
b.reduce(a) { |memo, e| memo.<<(e) }
will correctly update a array inplace, exactly as a.concat(b) would do:
#⇒ [[1,"a"], [2,"b"], [3,"c"], [4,"d"]]
the short notation all if a sudden raises an exception:
b.reduce(a) &:<<
#⇒ TypeError: [[1, "a"], [2, "b"]] is not a symbol
What am I missing? Ruby 2.1.
P.S. Casted by this question.

b.reduce(a) &:<<
won't work because it's not valid method calling. Instead, pass the symbol as the last argument:
b.reduce(a, &:<<)
# => [[1, "a"], [2, "b"], [3, "c"], [4, "d"]]
When you call:
[1,2,3].reduce &:+
&:+ is an argument to the method. It's actually equivalent to:
[1,2,3].reduce(&:+)
If the last argument to a method is preceded by &, it is a considered a Proc
object (the Symbol to Proc trick). Then it's removed from the parameter list, and is converted into a block , the method then associate the block.

Related

I need some clarification on how this 2D array can be created in Ruby?

I'm currently learning Ruby through App Academy Open, and came across a problem that I solved differently than the course solution. I could use some clarification on how the course solution works.
We have to define a function "zip" that takes any number of arrays as arguments (but all arrays the same length). The function should return a 2D array where each subarray contains the elements at the same index from each argument.
zip(['a','b','c'],[1,2,3])
should return:
[['a',1],['b',2],['c',3]]
Here is my solution:
def zip(*arrs)
main_arr = Array.new(arrs[0].length) {Array.new}
arrs.each do |array|
array.each_with_index do |ele, ele_idx|
main_arr[ele_idx] << ele
end
end
main_arr
end
And here is the course solution:
def zip(*arrays)
length = arrays.first.length
(0...length).map do |i|
arrays.map { |array| array[i] }
end
end
Can someone explain how the 2D array is being built within the mapped range above? I'm a bit confused as a beginner and could use some clarification.
EDIT:
Thank you very much iGian. Explanation really helped.
Running the code below should be self explanatory, see the comments:
def zip_steps(*arrays)
# get the size of the array
length = arrays.first.length
# mapping the range up to use as indexing
p (0...length).map { |i| i } #=> [0, 1, 2]
# map the arrays
p arrays.map { |array| array } #=> [["a", "b", "c"], [1, 2, 3]]
# map the arrays returning a specific element at index 1, for example
p arrays.map { |array| array[1] } #=> ["b", 2]
# put arrays mapping inside the range mapping
# where instead of returning the element 1
# it returns the element i
(0...length).map do |i|
arrays.map { |array| array[i] }
end
end
ary1 = ['a','b','c']
ary2 = [1,2,3]
p zip_steps(ary1, ary2) #=> [["a", 1], ["b", 2], ["c", 3]]

Sort an array of arrays by the number of same occurencies in Ruby

This question is different from this one.
I have an array of arrays of AR items looking something like:
[[1,2,3], [4,5,6], [7,8,9], [7,8,9], [1,2,3], [7,8,9]]
I would like to sort it by number of same occurences of the second array:
[[7,8,9], [1,2,3], [4,5,6]]
My real data are more complexes, looking something like:
raw_data = {}
raw_data[:grapers] = []
suggested_data = {}
suggested_data[:grapers] = []
varietals = []
similar_vintage.varietals.each do |varietal|
# sub_array
varietals << Graper.new(:name => varietal.grape.name, :grape_id => varietal.grape_id, :percent => varietal.percent)
end
raw_data[:grapers] << varietals
So, I want to sort raw_data[:grapers] by the max occurrencies of each varietals array comparing this value: grape_id inside them.
When I need to sort a classical array of data by max occurencies I do that:
grapers_with_frequency = raw_data[:grapers].inject(Hash.new(0)) { |h,v| h[v] += 1; h }
suggested_data[:grapers] << raw_data[:grapers].max_by { |v| grapers_with_frequency[v] }
This code doesn't work cos there are sub arrays there, including AR models that I need to analyze.
Possible solution:
array.group_by(&:itself) # grouping
.sort_by {|k, v| -v.size } # sorting
.map(&:first) # optional step, depends on your real data
#=> [[7, 8, 9], [1, 2, 3], [4, 5, 6]]
I recommend you take a look at the Ruby documentation for the sort_by method. It allows you to sort an array using anything associated with the elements, rather than the values of the elements.
my_array.sort_by { |elem| -my_array.count(elem) }.uniq
=> [[7, 8, 9], [1, 2, 3], [4, 5, 6]]
This example sorts by the count of each element in the original array. This is preceded with a minus so that the elements with the highest count are first. The uniq is to only have one instance of each element in the final result.
You can include anything you like in the sort_by block.
As Ilya has pointed out, having my_array.count(elem) in each iteration will be costlier than using group_by beforehand. This may or may not be an issue for you.
arr = [[1,2,3], [4,5,6], [7,8,9], [7,8,9], [1,2,3], [7,8,9]]
arr.each_with_object(Hash.new(0)) { |a,h| h[a] += 1 }.
sort_by(&:last).
reverse.
map(&:first)
#=> [[7.8.9]. [1,2,3], [4,5,6]]
This uses the form of Hash::new that takes an argument (here 0) that is the hash's default value.

While converting Array of array in ruby to hash hash does not include all the keys but takes the last one

This is an array which i want to convert into a hash
a = [[1, 3], [3, 2], [1, 2]]
but the hash i am getting is
2.2.0 :004 > a.to_h
=> {1=>2, 3=>2}
why is it so?
Hashes have unique keys. Array#to_h is effectively doing the following:
h = {}.merge(1=>3).merge(3=>2).merge(1=>2)
#=> { 1=>3 }.merge(3=>2).merge(1=>2)
#=> { 1=>3, 3=>2 }.merge(1=>2)
#=> { 1=>2, 3=>2 }
In the last merge the value of the key 1 (3) is replaced with 2.
Note that
h.merge(k=>v)
is (permitted) shorthand for
h.merge({ k=>v })
The keys of a Hash are basically a Set, so no duplicate keys are allowed.
If two pairs are present in your Array with the same first element, only the last pair will be kept in the Hash.
If you want to keep the whole information, you could define arrays as values :
a = [[1, 3], [3, 2], [1, 2]]
hash = Hash.new{|h,k| h[k] = []}
p a.each_with_object(hash) { |(k, v), h| h[k] << v }
#=> {1=>[3, 2], 3=>[2]}
Here's a shorter but less common way to define it :
hash = a.each_with_object(Hash.new{[]}) { |(k, v), h| h[k] <<= v }
Calling hash[1] returns [3,2], which are all the second elements from the pairs of your array having 1 as first element.

Using self.dup, but failing rspec test to not modify original array

I'm creating a method to transpose square 2-d arrays. My method passes every test, except the "does not modify original array" one. I'm only working on the duped array, so I'm confused on why the test is failing.
Code:
class Array
def my_transpose
orig_arr = self.dup; array = []
orig_arr[0].length.times do
temp_arr = []
orig_arr.each { |arr| temp_arr << arr.shift }
array << temp_arr
end
array
end
end
RSpec:
describe Array do
describe "#my_transpose" do
let(:arr) { [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
] }
let(:small_arr) { [
[1, 2],
[3, 4]
] }
it "transposes a small matrix" do
expect(small_arr.my_transpose).to eq([
[1, 3],
[2, 4]
])
end
it "transposes a larger matrix" do
expect(arr.my_transpose).to eq([
[1, 4, 7],
[2, 5, 8],
[3, 6, 9]
])
end
it "should not modify the original array" do
small_arr.my_transpose
expect(small_arr).to eq([
[1, 2],
[3, 4]
])
end
it "should not call the built-in #transpose method" do
expect(arr).not_to receive(:transpose)
arr.my_transpose
end
end
end
Output:
7) Array#my_transpose should not modify the original array
Failure/Error: expect(small_arr).to eq([
expected: [[1, 2], [3, 4]]
got: [[], []]
(compared using ==)
# ./spec/00_array_extensions_spec.rb:123:in `block (3 levels) in <top (required)>'
When you call dup on an array, it only duplicates the array itself; the array's contents are not also duplicated. So, for example:
a = [[1,2],[3,4]]
b = a.dup
a.object_id == b.object_id # => false
a[0].object_id == b[0].object_id # => true
Thus, modifications to a itself are not reflected in b (and vice versa), but modifications in the elements of a are reflected in b, because those elements are the same objects.
That being the case, the problem crops up here:
orig_arr.each { |arr| temp_arr << arr.shift }
arr is an element of orig_arr, but it is also an element of self. If you did something like remove it from orig_arr, you would not also remove it from self, but if you change it, it's changed, no matter how you are accessing it, and as it turns out, Array#shift is a destructive operation.
Probably the smallest change you could make to your code to make it work as you expect would be to use each_with_index, and then use the index into arr, rather than calling arr.shift, so:
orig_arr.each_with_index { |arr,i| temp_arr << arr[i] }
In fact, though, once you're doing that, you're not doing any destructive operations at all and you don't need orig_arr, you can just use self.
The original array isn’t being modified, but the arrays within it are, as dup is a shallow clone.
xs = [[1,2],[3,4]]
ids = xs.map(&:object_id)
xs.my_transpose
ids == xs.map(&:object_id) #=> true
Since shift is a mutating operation (being performed on the nested array elements), you need to dup the elements within the array as well, e.g.
orig_arr = dup.map(&:dup)
With this modification, your test should pass.

Rules on Parenthesis for Block Variables

I ran across the following piece of code while reading The Ruby Way:
class Array
def invert
each_with_object({}).with_index { |(elem, hash), index| hash[elem] = index }
end
end
I want to make sure that I understand what the parenthesis are doing in (elem, hash).
The first method (each_with_object({})) will yield two objects to the block. The first object will be the element in the array; the second object will be the hash. The parentheses make sure that those two objects are assigned to different block variables. If I had instead used { |elem, index} #code }, then elem would be an array consisting of the element and the hash. I think that is clear.
My confusion lies with the fact that if I didn't chain these two methods, I would not have to use the parentheses, and instead could use: each_with_object({}) { |elem, obj #code }.
What are the rules about when parentheses are necessary in block variables? Why do they differ between the two examples here? My simplistic explanation is that, when the methods are not chained, then the yield code looks like yield (elem, obj), but when the methods are chained, the code looks like yield([elem, obj], index). (We can surmise that a second array would be passed in if we chained a third method). Is this correct? Is the object(s) passed in from the last chained method not an array?
I guess instead of all this conjecture, the question boils down to: "What does the yield statement look like when chaining methods that accept blocks?
Your question is only tangentially concerned with blocks and block variables. Rather, it concerns the rules for "disambiguating" arrays.
Let's consider your example:
[1,2,3].each_with_object({}).with_index {|(elem, hash), index| hash[elem] = index}
We have:
enum0 = [1,2,3].each_with_object({})
#=> #<Enumerator: [1, 2, 3]:each_with_object({})>
We can see this enumerator's elements by converting it to an array:
enum0.to_a
#=> [[1, {}], [2, {}], [3, {}]]
We next have:
enum1 = enum0.with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:each_with_object({})>:with_index>
enum1.to_a
#=> [[[1, {}], 0], [[2, {}], 1], [[3, {}], 2]]
You might want to think of enum1 as a "compound enumerator", but it's just an enumerator.
You see that enum1 has three elements. These elements are passed to the block by Enumerator#each. The first is:
enum1.first
#=> [[1, {}], 0]
If we had a single block variable, say a, then
a #=> [[1, {}], 0]
We could instead break this down in different ways using "disambiguation". For example, we could write:
a,b = [[1, {}], 0]
a #=> [1, {}]
b #=> 0
Now let's stab out all the elements:
a,b,c = [[1, {}], 0]
a #=> [1, {}]
b #=> 0
c #=> nil
Whoops! That's not what we wanted. We've just experienced the "ambiguous" in "disambiguate". We need to write this so that our intentions are unambiguous. We do that by adding parenthesis. By doing so, you are telling Ruby, "decompose the array in this position to its constituent elements". We have:
(a,b),c = [[1, {}], 0]
a #=> 1
b #=> {}
c #=> 0
Disambiguation can be extremely useful. Suppose, for example, a method returned the array:
[[1,[2,3],[[4,5],{a: 6}]],7]
and we wish to pull out all the individual values. We could do that as follows:
(a,(b,c),((d,e),f)),g = [[1,[2,3],[[4,5],{a: 6}]],7]
a #=> 1
b #=> 2
c #=> 3
d #=> 4
e #=> 5
f #=> {:a=>6}
g #=> 7
Again, you just have to remember that the parentheses simply mean "decompose the array in this position to its constituent elements".
The rule is basic: every enumerator has a “signature.” E.g. it yields two parameters, then the proc to be passed should expect two parameters to receive:
[1,2,3].each_with_index { |o, i| ...}
When the object might be expanded, like hash item, it may be expanded using parenthesis. Assuming, the iterator yields an array, [*arr]-like operation is permitted with.
The following example might shed a light on this:
[1,2,3].each_with_object('first') # yielding |e, obj|
.with_index # yielding |elem, idx|
# but wait! elem might be expanded here ⇑⇑⇑⇑
# |(e, obj), idx|
.each_with_object('second') do |((elem, fst_obj), snd_idx), trd_obj|
puts "e: #{elem}, 1o: #{fst_obj}, 2i: #{snd_idx}, 3o: #{trd_obj}"
end
#⇒ e: 1, 1o: first, 2i: 0, 3o: second
#⇒ e: 2, 1o: first, 2i: 1, 3o: second
#⇒ e: 3, 1o: first, 2i: 2, 3o: second

Resources