Forum: Ruby some questions on ruby - case1(hash values changed unexpected)

7b3d60a2de005a458e45f2722345f3c6?d=identicon&s=25 Previn Lin (previn)
on 2013-10-24 10:24
#################################
1. cat case1.rb
#################################
#!/usr/bin/env ruby

tools_info_array = {
   'tool_a' => ['tool_a_home', '2009.12-17', 'bin', {'LD_LIBRARY_PATH'
=> 'lib'}]
                   }

toolName = 'tool_a'

tools_info = tools_info_array[toolName]

if tools_info.size > 3
        print "\nbefore delete:\n"
        puts "tools_info_array:\n", tools_info_array
        tools_info.slice!(0..2)
        print "\nafter delete:\n"
        puts "tools_info_array:\n", tools_info_array
end

tools_info.each do |xsub|
......
end

#################################
2. run case1.rb get results below
#################################
before delete:
tools_info_array:
{"tool_a"=>["tool_a_home", "2009.12-17", "bin",
{"LD_LIBRARY_PATH"=>"lib"}]}

after delete:
tools_info_array:
{"tool_a"=>[{"LD_LIBRARY_PATH"=>"lib"}]}

#################################
3. my question
#################################
Why modify 'tools_info' cause 'tools_info_array' changed?
Define a temp variable to save tools_info as below can avoid issue, but
is there other method can avoid using temp variable, that get
'tools_info' modified but without influence on 'tools_info_array'?

tools_info_tmp = tools_info.slice(3..-1)
tools_info_tmp.each do |xsub|
......
end
14b5582046b4e7b24ab69b7886a35868?d=identicon&s=25 Joel Pearson (virtuoso)
on 2013-10-24 11:18
It's this: "tools_info.slice!"

The clue is in the "!". It's a "bang" method, which means it modifies
the receiver.
7b3d60a2de005a458e45f2722345f3c6?d=identicon&s=25 Previn Lin (previn)
on 2013-10-24 11:32
Joel Pearson wrote in post #1125477:
> It's this: "tools_info.slice!"
>
> The clue is in the "!". It's a "bang" method, which means it modifies
> the receiver.

I know this, so I use "!", what I want is to modify 'tools_info' but
don't
change 'tools_info_array'.
14b5582046b4e7b24ab69b7886a35868?d=identicon&s=25 Joel Pearson (virtuoso)
on 2013-10-24 11:56
You could always use tap if you don't want a local variable hanging
around.

tools_info.slice(3..-1).tap { |tools_info_tmp| other_stuff  }
F549641069ea6946c5cd63bbfb451cd7?d=identicon&s=25 WILLS, JAMAL A (Guest)
on 2013-10-24 14:14
(Received via mailing list)
You can duplicate objects with the #dup method.  This creates a copy
without introducing a temporary variable.  The normal assignment
operation just copies a reference, so in your code tools_info refers to
the same object instance and not a copy.  (Also, I think #clone is an
alias of #dup.)

tools_info = tools_info_array[toolName].dup
F549641069ea6946c5cd63bbfb451cd7?d=identicon&s=25 WILLS, JAMAL A (Guest)
on 2013-10-24 14:29
(Received via mailing list)
Another way to handle it is to use #slice instead of #slice! as follows:

  tools_info = tools_info.slice(0..2)

This allows the slice method to do the duplication as well as the
slicing.

One way to define bang and non-bang method pairs is to make the safe
method duplicate the arguments of the unsafe (bang!) method:

  def some_method!(some_object)
    # do something dangerous to some_object here
  end

  def some_method(some_object)
    some_method!(some_object.dup)
  end

As you can see, some_method does the exact same thing as some_method!,
but on a duplicate of the original object.  This is an implicit way of
the explicit #dup I described before:

  tools_info = tools_info_array[toolName].dup

I suppose it depends on the desired level of code, memory, and
processing complexity desired.

Jamal Wills
14b5582046b4e7b24ab69b7886a35868?d=identicon&s=25 Joel Pearson (virtuoso)
on 2013-10-24 14:38
WILLS, JAMAL A wrote in post #1125494:
> Another way to handle it is to use #slice instead of #slice! as follows:
>
>   tools_info = tools_info.slice(0..2)

This doesn't do the same thing as #slice!, the return value is what is
removed, not the remainder.
F549641069ea6946c5cd63bbfb451cd7?d=identicon&s=25 WILLS, JAMAL A (Guest)
on 2013-10-24 14:43
(Received via mailing list)
Sorry,  I'm less familiar with that particular method.  In that case,
this may work better:

  tools_info = tools_info.dup.slice!(0..2)

Or this:

  tools_info = tools_info.slice(3..-1)

Jamal Wills
7b3d60a2de005a458e45f2722345f3c6?d=identicon&s=25 Previn Lin (previn)
on 2013-10-25 04:08
Hi WILLS,

Thanks for your help check this issue, now I understand, in ruby, the
default behavior of the array variable is a reference(or name it as a
pointer) to the array(even slice of array), so any change on the contect
of slice will cause the original array value changed, the safe way is
dupicate the slice, or just move the pointer to the start of the slice
data, am  I right?

Thanks a lot,
Previn
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.