Array Unexpected Multi Assignment - ruby

I have the following array:
#master = Array.new(4, Array.new(2, Array.new()))
=> [[[], []], [[], []], [[], []], [[], []]]
I'm attempting to assign the very first most value with:
#master[0][0] = "x"
=> "x"
But this is doing a multi assignment
#master
=> [["x", []], ["x", []], ["x", []], ["x", []]]
How do I assign only the first value? I'm hoping to get the following Array:
#master
=> [["x", []], [[], []], [[], []], [[], []]]

In that way you use the same reference for every sub array. Try this way
#master = Array.new(4) { Array.new(2) { Array.new } }

You are creating one array an assigning it to every element of the first array; try running this code:
#master.each { |e| puts e.object_id }
Output (your ids will be different):
70192803217260
70192803217260
70192803217260
70192803217260
As you can see, is the exact same object, so try using #master = Array.new(4) { Array.new(2) { Array.new() } } instead, which will create a new array for each item in the first array.

Related

How check in ruby if nested array is empty?

Response list returns empty nested array: [[], [], []]
How better test with ruby that following nested array is empty?
You can use Array#empty?
To check all nested arrays are empty with Array#all?
[[], [], []].all?(&:empty?)
# => true
[[1], [2], []].all?(&:empty?)
# => false
[[1], [2], [3]].all?(&:empty?)
# => false
To check at least one nested is empty with Array#any?
[[], [], []].any?(&:empty?)
# => true
[[1], [2], []].any?(&:empty?)
# => true
[[1], [2], [3]].any?(&:empty?)
# => false
If you want to handle deeply nested arrays, you probably want to flatten your array first:
[[], [], []].flatten.empty?
=> true
[[], [[], [[]]]].flatten.empty?
=> true
[[], [[], [[1]]]].flatten.empty?
=> false

Nested array in Ruby, updates all the array values instead of one

I tried to create a nested array, but when I update one of them, all other arrays seems to be updated, What am I doing wrong?
arr = Array.new(5,Array.new())
# => [[], [], [], [], []]
arr[0]
# => []
arr[0].push(1)
# => [1]
arr
# => [[1], [1], [1], [1], [1]]
You could use the block syntax to initialize the array:
arr = Array.new(5) { Array.new }
=> [[], [], [], [], []]
arr[0].push(1)
arr
=> [[1], [], [], [], []]
map produces the array:
arr = 5.times.map { [] }
arr.first << 42
#⇒ [42]
arr
#⇒ [[42], [], [], [], []]

Why Array.new(3, []) works differently than [[], [], []] in Ruby? [duplicate]

This question already has answers here:
Why does array.each behavior depend on Array.new syntax?
(3 answers)
Closed 5 years ago.
Can anyone explain this to me:
irb(main):001:0> a = Array.new(3, [])
=> [[], [], []]
irb(main):001:0> b = [[], [], []]
=> [[], [], []]
irb(main):003:0> a.each_with_index{ |r, idx| r << 'a' }
=> [["a", "a", "a"], ["a", "a", "a"], ["a", "a", "a"]]
irb(main):004:0> b.each_with_index{ |r, idx| r << 'a' }
=> [["a"], ["a"], ["a"]]
When using the .new method:
Since all the Array elements store the same hash, changes to one of them will affect them all.
If multiple copies are what you want, you should use the block version which uses the result of that block each time an element of the array needs to be initialized:
2.3.0 :001 > a = Array.new(3) { [] }
=> [[], [], []]
2.3.0 :002 > a.each_with_index{ |r, idx| r << 'a' }
=> [["a"], ["a"], ["a"]]
Read the examples here - https://ruby-doc.org/core-2.2.0/Array.html#method-c-new

Grouping an array on the basis of its first element, without duplication in Ruby

I'm executing an active record command Product.pluck(:category_id, :price), which returns an array of 2 element arrays:
[
[1, 500],
[1, 100],
[2, 300]
]
I want to group on the basis of the first element, creating a hash that looks like:
{1 => [500, 100], 2 => [300]}
group_by seems logical, but replicates the entire array. I.e. a.group_by(&:first) produces:
{1=>[[1, 500], [1, 100]], 2=>[[2, 300]]}
You can do a secondary transform to it:
Hash[
array.group_by(&:first).collect do |key, values|
[ key, values.collect { |v| v[1] } ]
end
]
Alternatively just map out the logic directly:
array.each_with_object({ }) do |item, result|
(result[item[0]] ||= [ ]) << item[1]
end
This one-liner seemed to work for me.
array.group_by(&:first).map { |k, v| [k, v.each(&:shift)] }.to_h
Since you're grouping by the first element, just remove it with shift and turn the result into a hash:
array.group_by(&:first).map do |key, value|
value = value.flat_map { |x| x.shift; x }
[key, value]
end #=> {1=>[500, 100], 2=>[300]}
I do not like the destructive operation.
array.group_by(&:first).map { |id, a| [id, a.map(&:last)] }.to_h
Used this functionality several times in my app, added extension to an array:
# config/initializers/array_ext.rb
class Array
# given an array of two-element arrays groups second element by first element, eg:
# [[1, 2], [1, 3], [2, 4]].group_second_by_first #=> {1 => [2, 3], 2 => [4]}
def group_second_by_first
each_with_object({}) { |(first, second), h| (h[first] ||= []) << second }
end
end

How do I do element-wise comparison of two arrays?

I have two arrays:
a = [1,2,3]
b = [1,4,3]
Is there an element-wise comparison method in Ruby such that I could do something like this:
a == b
returns:
[1,0,1] or something like [TRUE,FALSE,TRUE].
Here's one way that I can think of.
a = [1, 2, 3]
b = [1, 4, 3]
a.zip(b).map { |x, y| x == y } # [true, false, true]
You can also use .collect
a.zip(b).collect {|x,y| x==y }
=> [true, false, true]
a = [1,2,3]
b = [1,4,3]
a.zip(b).map { |pair| pair[0] <=> pair[1] }
=> [0, -1, 0]
The element-wise comparison is achieved with the zip Ruby Array object method.
a = [1,2,3]
b = [1,4,3]
a.zip(b)
=> [[1, 1], [2, 4], [3, 3]]
You can do something like this to get exactly what you want:
[1,2,3].zip([1,4,3]).map { |a,b| a == b }
=> [true, false, true]
This should do the trick:
array1.zip(array2).map { |a, b| a == b }
zip creates one array of pairs consisting of each element from both arrays at that position. Imagine gluing the two arrays side by side.
Try something like this :
#array1 = ['a', 'b', 'c', 'd', 'e']
#array2 = ['d', 'e', 'f', 'g', 'h']
#intersection = #array1 & #array2
#intersection should now be ['d', 'e'].
Intersection—Returns a new array containing elements common to the two arrays, with no duplicates.
You can even try some of the ruby tricks like the following :
array1 = ["x", "y", "z"]
array2 = ["w", "x", "y"]
array1 | array2 # Combine Arrays & Remove Duplicates(Union)
=> ["x", "y", "z", "w"]
array1 & array2 # Get Common Elements between Two Arrays(Intersection)
=> ["x", "y"]
array1 - array2 # Remove Any Elements from Array 1 that are
# contained in Array 2 (Difference)
=> ["z"]

Resources