Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does array.each behavior depend on Array.new syntax?

Tags:

ruby

I'm using Ruby 1.9.2-p290 and found:

a = Array.new(2, []).each {|i| i.push("a")}    
=> [["a", "a"], ["a", "a"]]

Which is not what I would expect. But the following constructor style does do what I would expect:

b = Array.new(2) {Array.new}.each {|i| i.push("b")}
=> [["b"], ["b"]] 

Is the first example the expected behavior?

In ruby-doc it looks like my size=2 argument is the same kind of argument for both constructors. I think that if the each method is getting passed that argument that it would use it the same way for both constructors.

like image 559
eeeeeean Avatar asked Jan 27 '12 16:01

eeeeeean


People also ask

What are the Behaviour of an array variable?

A Fortran array variable can be one of four major types: explicit-shape, assumed-shape, deferred-shape, and assumed-size. The runtime descriptor for the first three types makes that array variable's size known at compile time or at runtime. The last dimension of an assumed-size array is the length of its variable.

Why does changing an array in JavaScript affect copies of the array?

An array in JavaScript is also an object and variables only hold a reference to an object, not the object itself. Thus both variables have a reference to the same object.

Why do we need array explain with example?

Arrays are used when there is a need to use many variables of the same type. It can be defined as a sequence of objects which are of the same data type. It is used to store a collection of data, and it is more useful to think of an array as a collection of variables of the same type. Arrays can be declared and used.

What is the correct syntax for for in loop?

The syntax of the for...in loop is: for (key in object) { // body of for...in } In each iteration of the loop, a key is assigned to the key variable. The loop continues for all object properties. Note: Once you get keys, you can easily find their corresponding values.


2 Answers

This is a common misunderstanding. In your first example you are creating an array with 2 elements. Both of those are a pointer to the same array. So, when you iterate through your outer array you add 2 elements to the inner array, which is then reflected in your output twice

Compare these:

> array = Array.new(5, [])
=> [[], [], [], [], []] 

# Note - 5 identical object IDs (memory locations)
> array.map { |o| o.object_id }
=> [70228709214620, 70228709214620, 70228709214620, 70228709214620, 70228709214620] 

> array = Array.new(5) { [] }
=> [[], [], [], [], []] 

# Note - 5 different object IDs (memory locations)
> array.map { |o| o.object_id }
=> [70228709185900, 70228709185880, 70228709185860, 70228709185840, 70228709185780] 
like image 125
Gareth Avatar answered Nov 09 '22 22:11

Gareth


In the first case you're using a single instance of an Array as a default for the elements of the main Array:

a = Array.new(2, []).each {|i| i.push("a")}

The second argument is simply recycled, so the push is applied to the same instance twice. You've only created one instance here, the one being supplied as an argument, so it gets used over and over.

The second method is the correct way to do this:

b = Array.new(2) {Array.new}.each {|i| i.push("b")

This deliberately creates a new instance of an Array for each position in the main Array. The important difference here is the use of the block { ... } which executes once for each position in the new Array. A short-form version of this would be:

b = Array.new(2) { [ ] }.each {|i| i.push("b")
like image 23
tadman Avatar answered Nov 09 '22 20:11

tadman