Value inside array having same object id [duplicate] - ruby-on-rails

This question already has an answer here:
Ruby Array Initialization [duplicate]
(1 answer)
Closed 3 years ago.
Recently I was playing with arrays and found out a weird behavior. I created a new array using Array.new.
arr = Array.new(4,"happy")
["happy", "happy", "happy", "happy"]
I appended a word into the second element in that array like shown below
arr[1] <<" Dino"
When I looked at arr, I was expecting an array with the second element having an appended word. But to my surprise array returned with all elements with the appended word.
["happy Dino", "happy Dino", "happy Dino", "happy Dino"]
How can this happen? Are we creating copies of the same string when we are creating the array? It doesn't happen if we use arr[1]= " Dino". Somebody can explain why this is happening?

Yes, you a right. See the ruby docs
When a size and an optional default are sent, an array is created with size copies of default. Take notice that all elements will reference the same object default.
You can initialize array in a way like this:
arr = Array.new(4){ 'happy' }

When we take Array.new(4, "Happy") it will create 4 elements with same object id. We can observe in irb arr[1].object_id => 2880099 , arr[2].object_id => 2880099 and remaining 2 elements also will return same object id.
So when we try like arr[1] << "Something" , this string will append to all the same object id's.
Now if we take another element for arr like arr.push("newString"). Now if see the last element object id arr.last.object_id => 4889988,it's different now.
So if we try same command arr[1] << "Something" it won't be effect to newly inserted element because object id is different.
if you don't want this behaviour the use Array.new(4){ 'String' }

Related

Ruby Calling Join on an Array versus String

I have a script that creates an array , then adds items to the array depending on certain circumstances. In most cases, the array will end up with several values inside of it. Occasionally, the array will only hold one value inside of it.
After preparing this array, I usually call .join(",") to create a comma-separated string of all the array values:
tags.join(",")
It works fine when the array has multiple values, but when it only has one value it throws an error:
NoMethodError: undefined method 'join' for "Whatever the array value": String
Any idea why this is? What is the easiest way to resolve this? Do I need to do an if statement to check if the variable is an array or string? Seems a bit silly...let me know if I am missing something here.
If obj is your object, you can write
[*obj].join
For example
arr = ["Fa", "bu", "lo", "us!"]
[*arr].join #=> "Fabulous!"
str = "Whoa!"
[*str].join #=> "Whoa!"
This works because
[*arr] #=> ["Fa", "bu", "lo", "us!"] == arr
[*str] #=> ["Whoa!"]
Similarly,
[*[1,2,3]].join #=> "123"
[*7].join #=> "7"
You can use join on an array as following way :
#array = ["this","is","join","method","example"]
#array.join(" ")
"this is join method example"
#array.join("_")
"this_is_join_method_example"
In the case of a single element (say, 'Hello'), you should be calling join on an array, not the string itself; for example, ['Hello'].join(",") rather than 'Hello'.join(","). Of course, if there's only one element join doesn't actually do anything, so you could just use a conditional if to skip it... but that's kinda ugly. Most of the time, I'd use the construction Array(tags).join(","). If passed a single string, that'll wrap it in an array; if passed an array, it's a noop, returning the array as-is.

Hash and array interrelation on a value

I have an array like this,
array1 = [{"name"=>"Bob"}, {"age"=>"30"}]
Now, I used a condition to find an element from array where age is 30.
hash1 = array1.find { |h| h['age'] == "30" }, which gives a hash. --> {"age"=>30}
Now I changed the value from that hash as,
hash1['age'] = 31, so hash1 becomes --> {"age"=>31}
My question is when I again print the value in array, surprisingly the value of the age in the array updated to '31' and I just changed it in the hash object, but how the array value changed??
array1 --> became [{"name"=>"Bob"}, {"age"=>31}]
Is there any reason behind this.Thanks in advance. :-)
Yes, that's because it is doing a "shallow copy" of the object.A shallow copy means that only the fundamental datatypes actually get copied into new objects, everything else gets copied as a reference to the original object.
Check this link out for more details on the issue

How to create object array in rails?

I need to know how to create object array in rails and how to add elements in to that.
I'm new to ruby on rails and this could be some sort of silly question but I can't find exact answer for that. So can please give some expert ideas about this
All you need is an array:
objArray = []
# or, if you want to be verbose
objArray = Array.new
To push, push or use <<:
objArray.push 17
>>> [17]
objArray << 4
>>> [17, 4]
You can use any object you like, it doesn't have to be of a particular type.
Since everything is an object in Ruby (including numbers and strings) any array you create is an object array that has no limits on the types of objects it can hold. There are no arrays of integers, or arrays of widgets in Ruby. Arrays are just arrays.
my_array = [24, :a_symbol, 'a string', Object.new, [1,2,3]]
As you can see, an array can contain anything, even another array.
Depending on the situation, I like this construct to initialize an array.
# Create a new array of 10 new objects
Array.new(10) { Object.new }
#=> [#<Object:0x007fd2709e9310>, #<Object:0x007fd2709e92e8>, #<Object:0x007fd2709e92c0>, #<Object:0x007fd2709e9298>, #<Object:0x007fd2709e9270>, #<Object:0x007fd2709e9248>, #<Object:0x007fd2709e9220>, #<Object:0x007fd2709e91f8>, #<Object:0x007fd2709e91d0>, #<Object:0x007fd2709e91a8>]
Also if you need to create array of words next construction could be used to avoid usage of quotes:
array = %w[first second third]
or
array = %w(first second third)

Create and iterate through an array in Velocity Template Language

How to create an array in VTL and add contents to the array? Also how to retrieve the contents of the array by index?
According to Apache Velocity User Guide, right hand side of assignments can be of type
Variable reference
List item
String literal
Property reference
Method reference
Number literal
ArrayList
Map
You can create an empty list, which would satisfy all your needs for an array, in an Apache Velocity template with an expression like:
#set($foo = [])
or initialize values:
#set($foo = [42, "a string", 21, $myVar])
then, add elements using the Java add method:
$foo.add(53);
$foo.add("another string");
but beware, as the Java .add() method for the list type returns a boolean value, when you add an element to the list, Velocity will print, for instance, "true" or "false" based on the result of the "add" function.
A simple work around is assigning the result of the add function to a variable:
#set($bar = $foo.add(42))
You can access the elements of the list using index numbers:
<span>$foo[1]</span>
Expression above would show a span with the text "a string". However the safest way to access elements of a list is using foreach loops.
Creating an array is easy:
#set($array = [])
Putting an element into an array is also easy:
$array.add(23)
Getting an element from an array depends from your Velocity version.
In Velocity 1.6 you must use
$array.get($index)
Since Velocity 1.7 you can use the classic form:
$array[$index]
I haven't created an array in VTL but passed arrays to VTL context and used them. In VTL, you can not retrieve array contents by index, you only use foreach, as example this code is copied from my Dynamic SQL generation VTL Script:
#foreach( $col in $Columns ) SUM($col.DBColumn) AS ''$col.Name''#if($velocityCount!=$Columns.Count), #end #end
For this reason, we also can not have 2D arrays. When I needed an array to store 2 objects in a row, I used the workaround of defining a new class, and putting objects of that class in the single dimensional array.

get first value in hash within hash

Is there a simple way, instead of looping my entire array to fetch the first value of every inner array.
so in essence I have the following;
array = [['test', 'test2'...], ['test' ....]]
so I want to grab array[#][0] and store the unqiue values.
EDIT
Is there a similar way to use the transpose method for arrays with Hash?
I essentially want to do the same thing
Hash = {1=> {1=> 'test', .....}, 2=> {1=> 'test',....}
so at the end I want to have something like new hash variable and leave my existing hash within hash alone.... = {1 => 'test', 2=> 'test2'}
Not sure if I fully understand the question, but if you have a 2 dimensional array (array in array), and you want to turn that into an array of the first element of the second dimension, you can use the map function
firsts = array.map {|array2| array2.first}
The way map works is that it turns one collection into a second collection by applying a function you provide (the block) to each element.
Maybe this?
array.transpose[0]

Resources