Why does this code populate every array? - ruby

I have this piece of code that creates an array of arrays ([[],[],[]]) and an iterator that attempts to populate the respective arrays with numbers (shown below)
array = Array.new(3,[])
10.times do
array[0] << 2
array[1] << 3
array[2] << 4
end
When I execute this code, I expected to see this
[[2,2,2,2,etc....],[3,3,3,3,etc...],[4,4,4,4,4...etc]]
but instead I get this:
[[2,3,4,2,3,4,2,3,4....repeat],[2,3,4,2,3,4,2,3,4....repeat],[2,3,4,2,3,4,2,3,4....repeat]]
I've tried to walk through it with byebug and it makes no sense to me. What is going on here?

Array.new(3, []) is not equivalent to [[],[],[]]. It is equivalent to array = []; [array, array, array], an array of three references to the same array. After array[0] << 2, you have [[2], [2], [2]], because all three elements point at the same array.
What you want is Array.new(3) { [] }, with a block specifying the default; this way, a new [] is created for each element.
Even better, your whole code can be written as:
Array.new(3) { |i| Array.new(10, i + 2) }

The initializer you use is meant to be used with immutable objects, like creating an array of n times the same integer. For mutable objects use the block version. Here's the solution to your problem:
array = Array.new(3) { [] }
10.times do
array[0] << 2
array[1] << 3
array[2] << 4
end
array[0]
#=> [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]

Read the documentation
The second argument populates the array with references to the same
object. Therefore, it is only recommended in cases when you need to
instantiate arrays with natively immutable objects such as Symbols,
numbers, true or false.
To create an array with separate objects a block can be passed
instead. This method is safe to use with mutable objects such as
hashes, strings or other arrays:
Array.new(4) { Hash.new } #=> [{}, {}, {}, {}]
This is also a quick way to build up multi-dimensional arrays:
empty_table = Array.new(3) { Array.new(3) }
Therefore, your code first line should be
array = Array.new(3) { Array.new(3) }

Related

Adding unparallel elements between two arrays

I have a pair of arrays,
array_1 = [1,2,3,4,5]
array_2 = [10,9,8,7,6]
and I'm trying to subtract the nth element of one array from the (n-1)-th element of the second array, starting with the n-th element, yielding an array of:
[9-1, 8-2, 7-3, 6-4] = [8, 6, 4, 2]
I wrote it in a procedural fashion:
array_1.pop
array_2.shift
[array_2,array_1].transpose.map { |a,b| a-b }
but I do not wish to alter the arrays. Is there a method or another way to go about this?
Another way:
enum1 = array_1.to_enum
enum2 = array_2.to_enum
enum2.next
arr = []
loop do
arr << enum2.next - enum1.next
end
arr
#=> [8, 6, 4, 2]
Use the non-destructive drop for the receiver, and zip, which will stop when the receiver runs out of an element even if the argument has more.
array_2.drop(1).zip(array_1).map{|a, b| a - b}
I think you may be overthinking it a bit; as long as both arrays are the same length, you can just iterate over the indices you care about, and reference the other array by index - offset.
array_1 = [1,2,3,4,5]
array_2 = [10,9,8,7,6]
n = 1
(n...array_1.length).map {|i| array_2[i] - array_1[i - 1] }
You can set n to whatever number you like and compute from that point onwards, so even if the arrays were tremendously large, you don't have to generate any intermediate arrays, and you don't have to perform any unnecessary work.

Rand method on array with use of range operator + array index

I have this little problem with this code: I want to pass parameter to this random_select method in form of array, and I want to have one random index returned. I know that It won't work due to the way that range operator works , for such purposes we have sample method. But can anyone explain me why is this code returning nil as one of its random value?
def random_select(array)
array[rand(array[0]..array[4])]
end
p random_select([1,2,3,4,5])
Because your range is accessing to array values, not to array indexes:
array = [1, 2, 3, 4, 5]
array[0] #=> 1
array[4] #=> 5
array[0]..array[4] #=> 1..5
What you want is achievable in this way:
def random_select(array)
indexes = 0...array.size #=> 0...5
array[rand(indexes)]
end
array = [1, 2, 3, 4, 5]
Array.new(100) { random_select array }.uniq.sort == array #=> true
Have you tried using sample?
def random_select(array)
array.sample
end
Choose a random element or n random elements from the array.
The elements are chosen by using random and unique indices into the
array in order to ensure that an element doesn’t repeat itself unless
the array already contained duplicate elements.
If the array is empty the first form returns nil and the second form
returns an empty array.
The optional rng argument will be used as the random number generator.

Each with index with object in Ruby

I am trying to iterate over an array and conditionally increment a counter. I am using index to compare to other array's elements:
elements.each_with_index.with_object(0) do |(element, index), diff|
diff += 1 unless other[index] == element
end
I can't get diff to change value even when changing it unconditionally.
This can be solved with inject:
elements.each_with_index.inject(0) do |diff, (element, index)|
diff += 1 unless other[index] == element
diff
end
But I am wondering if .each_with_index.with_object(0) is a valid construction and how to use it?
From ruby docs for each_with_object
Note that you can’t use immutable objects like numbers, true or false
as the memo. You would think the following returns 120, but since the
memo is never changed, it does not.
(1..5).each_with_object(1) { |value, memo| memo *= value } # => 1
So each_with_object does not work on immutable objects like integer.
You want to count the number of element wise differences, right?
elements = [1, 2, 3, 4, 5]
other = [1, 2, 0, 4, 5]
# ^
I'd use Array#zip to combine both arrays element wise and Array#count to count the unequal pairs:
elements.zip(other).count { |a, b| a != b } #=> 1

Overriding elements of an array in "each" loop

a = [1, 2, 3]
a.each do |x| x+=10 end
After this operation array a is still [1, 2, 3]. How to convert it into [11, 12, 13]?
Use the collect! method:
a = [1, 2, 3]
a.collect!{ |x| x + 10 }
There are two general classes of solutions:
Imperative object-mutating code
a.map! { |x| x + 10 }
An almost functional solution
a = a.map { |x| x + 10 }
Both techniques have their place.
I like the aliased name "map" myself. It has less characters.
The difference with these methods as compared to what you've done is two fold. One is that you have to use a method that modifies the initial array (typically these are the bang methods, or the methods which have a name ending in a ! (map!, collect!, ...) The second thing is that a.each is the method typically used for just going through the array to use the individual elements. Map or Collect methods return an array containing a return from each iteration of the block.
Hence, you could have done the following:
a = [1,2,3]
b = []
a.each do |x|
b << x+10
end
or you could use the map or collect method as demonstrated by dmarko or as here:
a = [1,2,3]
a = a.map {|x| x+10}

what's different between each and collect method in Ruby [duplicate]

This question already has answers here:
Array#each vs. Array#map
(7 answers)
Closed 6 years ago.
From this code I don't know the difference between the two methods, collect and each.
a = ["L","Z","J"].collect{|x| puts x.succ} #=> M AA K
print a.class #=> Array
b = ["L","Z","J"].each{|x| puts x.succ} #=> M AA K
print b.class #=> Array
Array#each takes an array and applies the given block over all items. It doesn't affect the array or creates a new object. It is just a way of looping over items. Also it returns self.
arr=[1,2,3,4]
arr.each {|x| puts x*2}
Prints 2,4,6,8 and returns [1,2,3,4] no matter what
Array#collect is same as Array#map and it applies the given block of code on all the items and returns the new array. simply put 'Projects each element of a sequence into a new form'
arr.collect {|x| x*2}
Returns [2,4,6,8]
And In your code
a = ["L","Z","J"].collect{|x| puts x.succ} #=> M AA K
a is an Array but it is actually an array of Nil's [nil,nil,nil] because puts x.succ returns nil (even though it prints M AA K).
And
b = ["L","Z","J"].each{|x| puts x.succ} #=> M AA K
also is an Array. But its value is ["L","Z","J"], because it returns self.
Array#each just takes each element and puts it into the block, then returns the original array. Array#collect takes each element and puts it into a new array that gets returned:
[1, 2, 3].each { |x| x + 1 } #=> [1, 2, 3]
[1, 2, 3].collect { |x| x + 1 } #=> [2, 3, 4]
each is for when you want to iterate over an array, and do whatever you want in each iteration. In most (imperative) languages, this is the "one size fits all" hammer that programmers reach for when you need to process a list.
For more functional languages, you only do this sort of generic iteration if you can't do it any other way. Most of the time, either map or reduce will be more appropriate (collect and inject in ruby)
collect is for when you want to turn one array into another array
inject is for when you want to turn an array into a single value
Here are the two source code snippets, according to the docs...
VALUE
rb_ary_each(VALUE ary)
{
long i;
RETURN_ENUMERATOR(ary, 0, 0);
for (i=0; i<RARRAY_LEN(ary); i++) {
rb_yield(RARRAY_PTR(ary)[i]);
}
return ary;
}
# .... .... .... .... .... .... .... .... .... .... .... ....
static VALUE
rb_ary_collect(VALUE ary)
{
long i;
VALUE collect;
RETURN_ENUMERATOR(ary, 0, 0);
collect = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_ary_push(collect, rb_yield(RARRAY_PTR(ary)[i]));
}
return collect;
}
rb_yield() returns the value returned by the block (see also this blog post on metaprogramming).
So each just yields and returns the original array, while collect creates a new array and pushes the results of the block into it; then it returns this new array.
Source snippets: each, collect
The difference is what it returns. In your example above
a == [nil,nil,nil] (the value of puts x.succ) while b == ["L", "Z", "J"] (the original array)
From the ruby-doc, collect does the following:
Invokes block once for each element of
self. Creates a new array containing
the values returned by the block.
Each always returns the original array. Makes sense?
Each is a method defined by all classes that include the Enumerable module. Object.eachreturns a Enumerable::Enumerator Object. This is what other Enumerable methods use to iterate through the object. each methods of each class behaves differently.
In Array class when a block is passed to each, it performs statements of the block on each element, but in the end returns self.This is useful when you don't need an array, but you maybe just want to choose elements from the array and use the as arguments to other methods. inspect and map return a new array with return values of execution of the block on each element. You can use map! and collect! to perform operations on the original array.
I think an easier way to understand it would be as below:
nums = [1, 1, 2, 3, 5]
square = nums.each { |num| num ** 2 } # => [1, 1, 2, 3, 5]
Instead, if you use collect:
square = nums.collect { |num| num ** 2 } # => [1, 1, 4, 9, 25]
And plus, you can use .collect! to mutate the original array.

Resources