Behavior of map and each for 2D arrays - ruby

While each does not change the values of a given row in a 2d array (unlike map):
test_array = [[0,0],[0,0],[0,0]]
test_array[0].each {|e| e = 1}
test_array # => [[0, 0], [0, 0], [0, 0]]
test_array[0].map! {|e| e = 1}
test_array # => [[1, 1], [0, 0], [0, 0]]
each changes the values of a given column (in a way different from map):
test_array = [[0,0],[0,0],[0,0]]
test_array.each {|row| row[0] = 2}
test_array # => [[2, 0], [2, 0], [2, 0]]
test_array.map! {|row| row[0] = 2}
test_array # => [2, 2, 2]
Can somebody explain what is happening?

That's because, in Ruby, parameters are 'passed by object reference'.
In the case below,
test_array.each {|row| row[0] = 2}
row refers to an array inside test_array, and modifying its contents will reflect in test_array. This mutates the sub-array present in test_array.
In the case below,
test_array[0].each {|e| e = 1}
e refers to an integer in array[0], and making it point to a different integer will not impact the original value as we are not mutating the integer values present in array[0].
More details here: Is Ruby pass by reference or by value?
Regarding your query on map, please note that you are using map!, which will mutate the object on which the method is called, whereas map returns the value.
map! {|item| block } → ary
Invokes the given block once for each element of self, replacing the element with the value returned by the block.
Output of map is returned as is:
test_array.map {|row| row[0] = 2}
test_array # => [[2, 0], [2, 0], [2, 0]]
test_array is replaced with output of map:
test_array.map! {|row| row[0] = 2}
test_array # => [2, 2, 2]

Related

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]}

Finding the mode of a Ruby Array (simplified_

I'm trying to find the mode of an Array. Mode = the element(s) that appear with the most frequency.
I know there are lots of tricks with #enumerable, but I'm not there yet in my learning. The exercise I'm doing assumes I can solve this problem without understanding enumerable.
I've written out my game plan, but I'm stuck on the 2nd part. I'm not sure if it's possible to compare a hash key against an array, and if found, increment the value.
def mode(array)
# Push array elements to hash. Hash should overwrite dup keys.
myhash = {}
array.each do |x|
myhash[x] = 0
end
# compare Hash keys to Array. When found, push +=1 to hash's value.
if myhash[k] == array[x]
myhash[k] += 1
end
# Sort hash by value
# Grab the highest hash value
# Return key(s) per the highest hash value
# rejoice!
end
test = [1, 2, 3, 3, 3, 4, 5, 6, 6, 6]
mode(test) # => 3, 6 (because they each appear 3 times)
You can create a hash with a default initial value:
myhash = Hash.new(0)
Then increment specific occurrences:
myhash["foo"] += 1
myhash["bar"] += 7
myhash["bar"] += 3
p myhash # {"foo"=>1, "bar"=>10}
With that understanding, if you replace your initial hash declaration and then do the incrementing in your array.each iterator, you're practically done.
myhash.sort_by{|key,value| value}[-1]
gives the last entry in the sorted set of hash values, which should be your mode. Note that there may be multiple modes, so you can iterate backwards while the value portion remains constant to determine them all.
There are many, many ways you could do this. Here are a few.
#1
array = [3,1,4,5,4,3]
a = array.uniq #=> [3, 1, 4, 5]
.map {|e| [e, array.count(e)]}
#=> [[3, 2], [1, 1], [4, 2], [5, 1]]
.sort_by {|_,cnt| -cnt} #=> [[3, 2], [4, 2], [1, 1], [5, 1]]
a.take_while {|_,cnt| cnt == a.first.last}
#=> [[3, 2], [4, 2]]
.map(&:first) #=> [3, 4]
#2
array.sort #=> [1, 3, 3, 4, 4, 5]
.chunk {|e| e}
#<Enumerator: #<Enumerator::Generator:0x000001021820b0>:each>
.map { |e,a| [e, a.size] } #=> [[1, 1], [3, 2], [4, 2], [5, 1]]
.sort_by { |_,cnt| -cnt } #=> [[4, 2], [3, 2], [1, 1], [5, 1]]
.chunk(&:last)
#<Enumerator: #<Enumerator::Generator:0x00000103037e70>:each>
.first #=> [2, [[4, 2], [3, 2]]]
.last #=> [[4, 2], [3, 2]]
.map(&:first) #=> [4, 3]
#3
h = array.each_with_object({}) { |e,h|
(h[e] || 0) += 1 } #=> {3=>2, 1=>1, 4=>2, 5=>1}
max_cnt = h.values.max #=> 2
h.select { |_,cnt| cnt == max_cnt }.keys
#=> [3, 4]
#4
a = array.group_by { |e| e } #=> {3=>[3, 3], 1=>[1], 4=>[4, 4], 5=>[5]}
.map {|e,ees| [e,ees.size]}
#=> [[3, 2], [1, 1], [4, 2], [5, 1]]
max = a.max_by(&:last) #=> [3, 2]
.last #=> 2
a.select {|_,cnt| cnt == max}.map(&:first)
#=> [3, 4]
In your approach, you have first initialized a hash containing keys taken from the unique values of the array, with the associated values all set to zero. For example, the array [1,2,2,3] would create the hash {1: 0, 2: 0, 3: 0}.
After this, you plan to count the instances of each of the values in the array by incrementing the value for the associated key in the hash by one for each instance. So, after finding the number 1 in the array, the hash would look like so: {1: 1, 2: 0, 3: 0}. You clearly need to do this for each value in the array, so given your approach and current level of understanding, I would suggest looping through the array again:
array.each do |x|
myhash[x] += 1
end
As you can see, we don't need to check that myhash[k] == array[x] since we have already created a key:value pair for each number in the array.
However, while this approach will work, it's not very efficient: we're having to loop through the array twice. The first time to initialize all the key:value pairs to some default (zero, in this case), and the second to count the frequencies of each number.
Since the default value for each key will be zero, we can remove the need to initialize the defaults by using a different hash constructor. myhash = {} will return nil if we access a key that doesn't exist, but myhash = Hash.new(0) will return 0 if we access a non-existent key (note that you could provide any other value or variable, if required).
By providing a default value of zero, we can get rid of the first loop entirely. When the second loop finds a key that doesn't exist, it will use the default provided and automatically initialize it.
def mode(array)
array.group_by{ |e| e }.group_by{ |k, v| v.size }.max.pop.map{ |e| e.shift }
end
Using the simple_stats gem:
test = [1, 2, 3, 3, 3, 4, 5, 6, 6, 6]
test.modes #=> [3, 6]
If it is an unsorted array, we can sort the array in descending order
array = array.sort!
Then use the sorted array to create a hash default 0 and with each element of the array as a key and number of occurrence as the value
hash = Hash.new(0)
array.each {|i| hash[i] +=1 }
Then mode will be the first element if the hash is sorted in descending order of value(number of occurrences)
mode = hash.sort_by{|key, value| -value}.first[0]

Ruby enumerator chaining

In this example,
[1, 2, 3].each_with_index.map{|i, j| i * j}
# => [0, 2, 6]
my understanding is that, since each_with_index enumerator is chained to map, map behaves like each_with_index by passing an index inside the block, and returns a new array.
For this,
[1, 2, 3].map.each_with_index{|i, j| i * j}
# => [0, 2, 6]
I'm not sure how to I interpret it.
In this example,
[1, 2, 3, 4].map.find {|i| i == 2}
# => 2
I was expecting the the output to be [2], assuming that map is chained to find, and map would return a new array.
Also, I see this:
[1, 2, 3, 4].find.each_with_object([]){|i, j| j.push(i)}
# => [1]
[1, 2, 3, 4].each_with_object([]).find{|i, j| i == 3}
# => [3, []]
Can you let me know how to interpret and understand enumerator chains in Ruby?
You might find it useful to break these expressions down and use IRB or PRY to see what Ruby is doing. Let's start with:
[1,2,3].each_with_index.map { |i,j| i*j }
Let
enum1 = [1,2,3].each_with_index
#=> #<Enumerator: [1, 2, 3]:each_with_index>
We can use Enumerable#to_a (or Enumerable#entries) to convert enum1 to an array to see what it will be passing to the next enumerator (or to a block if it had one):
enum1.to_a
#=> [[1, 0], [2, 1], [3, 2]]
No surprise there. But enum1 does not have a block. Instead we are sending it the method Enumerable#map:
enum2 = enum1.map
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:each_with_index>:map>
You might think of this as a sort of "compound" enumerator. This enumerator does have a block, so converting it to an array will confirm that it will pass the same elements into the block as enum1 would have:
enum2.to_a
#=> [[1, 0], [2, 1], [3, 2]]
We see that the array [1,0] is the first element enum2 passes into the block. "Disambiguation" is applied to this array to assign the block variables the values:
i => 1
j => 0
That is, Ruby is setting:
i,j = [1,0]
We now can invoke enum2 by sending it the method each with the block:
enum2.each { |i,j| i*j }
#=> [0, 2, 6]
Next consider:
[1,2,3].map.each_with_index { |i,j| i*j }
We have:
enum3 = [1,2,3].map
#=> #<Enumerator: [1, 2, 3]:map>
enum3.to_a
#=> [1, 2, 3]
enum4 = enum3.each_with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:map>:each_with_index>
enum4.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum4.each { |i,j| i*j }
#=> [0, 2, 6]
Since enum2 and enum4 pass the same elements into the block, we see this is just two ways of doing the same thing.
Here's a third equivalent chain:
[1,2,3].map.with_index { |i,j| i*j }
We have:
enum3 = [1,2,3].map
#=> #<Enumerator: [1, 2, 3]:map>
enum3.to_a
#=> [1, 2, 3]
enum5 = enum3.with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:map>:with_index>
enum5.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum5.each { |i,j| i*j }
#=> [0, 2, 6]
To take this one step further, suppose we had:
[1,2,3].select.with_index.with_object({}) { |(i,j),h| ... }
We have:
enum6 = [1,2,3].select
#=> #<Enumerator: [1, 2, 3]:select>
enum6.to_a
#=> [1, 2, 3]
enum7 = enum6.with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:select>:with_index>
enum7.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum8 = enum7.with_object({})
#=> #<Enumerator: #<Enumerator: #<Enumerator: [1, 2, 3]:
# select>:with_index>:with_object({})>
enum8.to_a
#=> [[[1, 0], {}], [[2, 1], {}], [[3, 2], {}]]
The first element enum8 passes into the block is the array:
(i,j),h = [[1, 0], {}]
Disambiguation is then applied to assign values to the block variables:
i => 1
j => 0
h => {}
Note that enum8 shows an empty hash being passed in each of the three elements of enum8.to_a, but of course that's only because Ruby doesn't know what the hash will look like after the first element is passed in.
Methods you are mentioning are defined on Enumerable objects. These methods behave differently depending on whether you pass a block or not.
When you do not pass a block, they typically return an Enumerator object, to which you can chain further methods like each_with_index, with_index, map, etc.
When you pass a block to these methods, they return different kinds of object depending on what will make sense for that particular method.
For methods like find, its purpose is to find the first object that satisfies a condition, and it does not make particular sense to wrap that in an array, so it returns that object bare.
For methods like select or reject, their purpose is to return all relevant objects, so they cannot return a single object, and they have to be wrapped in an array (even when the relevant object happens to be a single object).

Replace array elements with map

I have two arrays:
#a = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
#b = [a, b, c]
I need to replace n-th column in a with b like:
swap_column(0)
#=> [a, 2, 3]
[b, 5, 6]
[c, 8, 9]
(This is for using Cramer's rule for solving equations system, if anybody wonders.)
The code I've come up with:
def swap_column(n)
#a.map.with_index { |row, j| row[n] = #b[j] }
end
How do I get rid of assignment here so that map returns the modified matrix while leaving #a intact?
What you wanted is dup. Also, you had the return value of the map.with_index block wrong.
def swap_column(i)
#a.map.with_index{|row, j| row = row.dup; row[i] = #b[j]; row}
end
or
def swap_column(i)
#a.map.with_index{|row, j| row.dup.tap{|row| row[i] = #b[j]}}
end
The answer by sawa is good and the main point is you need to dup your inner arrays for this to work properly. The only reason for this additional post is to point out that often when you are using with_index so that you can directly 1:1 index into another array you can simplify the code by using zip.
def swap_column(n)
#a.zip(#b).map {|r,e| r.dup.tap{|r| r[n] = e}}
end
What zip does is combine your two arrays into a new array where each element is an array made of the two corresponding elements of the initial arrays. In this case it would be an array of an array and an element you want to later use for replacement. We then map over those results and automatically destructure each element into the two pieces. We then dup the array piece and tap it to replace the nth element.
You can use transpose to do the following:
class M
attr :a, :b
def initialize
#a = [[1,2,3],
[4,5,6],
[7,8,9]
]
#b = [:a, :b, :c]
end
def swap_column(n)
t = #a.transpose
t[0] = #b
t.transpose
end
end
m = M.new
=> #<M:0x007ffdc2952e38 #a=[[1, 2, 3], [4, 5, 6], [7, 8, 9]], #b=[:a, :b, :c]>
m.swap_column(0)
=> [[:a, 2, 3], [:b, 5, 6], [:c, 8, 9]]
m # m is unchanged
=> #<M:0x007ffdc2952e38 #a=[[1, 2, 3], [4, 5, 6], [7, 8, 9]], #b=[:a, :b, :c]>

Creating and iterating a 2d array in Ruby

I have very little knowledge about Ruby and cant find a way to create 2d array. Can anyone provide me some snippets or information to get me started?
a = [[1, 2], [3, 4]]
a.each do |sub|
sub.each do |int|
puts int
end
end
# Output:
# 1
# 2
# 3
# 4
or:
a = [[1, 2], [3, 4]]
a.each do |(x, y)|
puts x + y
end
# Output:
# 3
# 7
The easiest way to create a 2d array is by the following:
arr1 = Array.new(3) { Array.new(3)}
The code above creates a 2D array with three rows and three columns.
Cheers.
irb(main):001:0> a = []
=> []
irb(main):002:0> a1 = [1, 2]
=> [1, 2]
irb(main):003:0> a2 = [3, 4]
=> [3, 4]
irb(main):004:0> a.push a1
=> [[1, 2]]
irb(main):005:0> a.push a2
=> [[1, 2], [3, 4]]
irb(main):006:0> a
=> [[1, 2], [3, 4]]
irb(main):007:0> a[0]
=> [1, 2]
irb(main):008:0> a[0][1]
=> 2
Ruby doesn't have the concept of 2-dimensional arrays like C does. Arrays in Ruby are dynamic -- meaning you can resize them a will. They can contain any object or value in each "slot" - including another Array!
In the examples given by #JunaidKirkire and #simonmenke, you have an array which has arrays for its values. You can access the values using the syntax similar to C - but you could also have the case where one slot is an Array and another is just a number, or a String, or a Hash...
You may want to work through a Ruby tutorial to get a better idea of how it works. I like RubyMonk but there are other good ones out there as well.
Creating a 2d array in Ruby
In ruby, every method accepts a block.
two_d_array = Array.new(3) do
Array.new(3) do
0
end
end
The same can be written In oneline (macro style)
two_d_array = Array.new(3) { Array.new(3) { 0 } }
Output
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
]
Iterating a 2d array in Ruby
I can think of three ways.
1: Using range
(0...two_d_array.size).each do |i|
(0...(two_d_array[i].length)).each do |j|
puts two_d_array[i][j]
end
end
2: Using for
for i in (0...two_d_array.size)
for j in (0...two_d_array[i].length)
puts two_d_array[i][j]
end
end
3: Using Each_with_index method
two_d_array.each_with_index do |sub_array, i|
sub_array.each_with_index do |item, j|
puts two_d_array[i][j]
end
end

Resources