I am creatinig a "constant" array containing hashes. Then, in the code, I create a new array based on the constant array. The hashes in the new array are then manipulated in the code. Here is some psudo- code showing what I am doing. TAB_SET = [ {<hash1>}, {<hash2>}, ... {<hashn>} ] def process_tabset tabs = Array.new(TAB_SET) <Process the tabs array here, modifying the hash values as needed> end Apparently what happens is that the new array contains REFERENCES to the original hashes instead of separate copies. Whatever changes are made to hashes in the "tabs" array, also shows up in the hashes in the TAB_SET array. In development mode (with config.cache_classes = false) this doesn''t matter, as the whole thing gets re-initialized on the next call anyway. But in Production mode, this causes all sorts of problems. How do I create the tabs array with copies of the hashes, instead of references back to the originals? Thanks, sjf --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
sjf_control wrote:> TAB_SET = [ {<hash1>}, {<hash2>}, ... {<hashn>} ] > > def process_tabset > tabs = Array.new(TAB_SET) > <Process the tabs array here, modifying the hash values as needed> > end > > Apparently what happens is that the new array contains REFERENCES to > the original hashes instead of separate copies.According to the pickaxe book it should be a copy. Perhaps try tabs = Array.new.concat(TAB_SET) --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
rein.henrichs-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
2007-Sep-03 06:00 UTC
Re: How to "New" an array of Hashes
TAB_SET.dup I won''t ask why you would actually want to do this. Rein On Sep 3, 12:02 am, Anthony Richardson <anth...-fLWHnXb90il4ktuDPxIMMA@public.gmane.org> wrote:> sjf_control wrote: > > TAB_SET = [ {<hash1>}, {<hash2>}, ... {<hashn>} ] > > > def process_tabset > > tabs = Array.new(TAB_SET) > > <Process the tabs array here, modifying the hash values as needed> > > end > > > Apparently what happens is that the new array contains REFERENCES to > > the original hashes instead of separate copies. > > According to the pickaxe book it should be a copy. Perhaps try > > tabs = Array.new.concat(TAB_SET)--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
On Sep 3, 12:02 am, Anthony Richardson <anth...-fLWHnXb90il4ktuDPxIMMA@public.gmane.org> wrote:> According to the pickaxe book it should be a copy. Perhaps try > > tabs = Array.new.concat(TAB_SET)Nope -- doesn''t work. irb(main):001:0> TAB_SET = [{:one=>"uno"},{:two=>"dos"}] => [{:one=>"uno"}, {:two=>"dos"}] irb(main):002:0> tabs = Array.new.concat(TAB_SET) => [{:one=>"uno"}, {:two=>"dos"}] irb(main):003:0> tabs[1][:two] += "(2)" => "dos(2)" irb(main):004:0> tabs => [{:one=>"uno"}, {:two=>"dos(2)"}] irb(main):005:0> TAB_SET => [{:one=>"uno"}, {:two=>"dos(2)"}] irb(main):006:0> --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
On Sep 3, 1:00 am, rein.henri...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote:> TAB_SET.dup > > I won''t ask why you would actually want to do this.Well, why I would want to do this isn''t really relevant, unless you have a better way. In short, each array entry contains information about a folder-tab on a page. One of the hashes contains the tab''s label, and I want to be able to dynamically update (append to) the label based on the contents of that page. In the example below, though, I would get "dos(2)" the first time (correct), and "dos(2)(2)" the second, and so forth. But in any case, "dup" doesn''t work either... irb(main):001:0> TAB_SET = [{:one=>"uno"},{:two=>"dos"}] => [{:one=>"uno"}, {:two=>"dos"}] irb(main):002:0> tabs = TAB_SET.dup => [{:one=>"uno"}, {:two=>"dos"}] irb(main):003:0> tabs[1][:two] += "(2)" => "dos(2)" irb(main):004:0> tabs => [{:one=>"uno"}, {:two=>"dos(2)"}] irb(main):005:0> TAB_SET => [{:one=>"uno"}, {:two=>"dos(2)"}] I believe the problem is NOT that the array is a reference. Using either "new" or "dup" should get me a fresh array. The problem is that the array ELEMENTS (the hashes) are references back to the original hashes. So I''m guessing I need something like: tabs = Array.new( Hash.new(hash1), Hash.new(hash2), ... ) But that sure isn''t very pretty! Anybody know an easier way? sjf --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
In a situation like this, where you are using basic data structures and defining all this business logic external to them to control their use, the ruby way would be to encapsulate all this logic in a class and use it like: @tabs = TabSet.new( options ) This way your TabSet class encapsulates all the logic of initializing and controlling a set of tabs (whatever that may be). You can use intention-revealing methods on that class and its instances to achieve its goals. Otherwise, and I hesitate to suggest this for fear that you might actually do it, you would need a deep copy: @tabs = Marshal.load(Marshal.dump(TAB_SET)) But please please please please don''t actually do that. It''s ugly as all hell. Rein On Sep 3, 10:21 am, sjf_control <sjfcont...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> On Sep 3, 1:00 am, rein.henri...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote: > > > TAB_SET.dup > > > I won''t ask why you would actually want to do this. > > Well, why I would want to do this isn''t really relevant, unless you > have a better way. > > In short, each array entry contains information about a folder-tab on > a page. One of the hashes contains the tab''s label, and I want to be > able to dynamically update (append to) the label based on the contents > of that page. In the example below, though, I would get "dos(2)" the > first time (correct), and "dos(2)(2)" the second, and so forth. > > But in any case, "dup" doesn''t work either... > > irb(main):001:0> TAB_SET = [{:one=>"uno"},{:two=>"dos"}] > => [{:one=>"uno"}, {:two=>"dos"}] > irb(main):002:0> tabs = TAB_SET.dup > => [{:one=>"uno"}, {:two=>"dos"}] > irb(main):003:0> tabs[1][:two] += "(2)" > => "dos(2)" > irb(main):004:0> tabs > => [{:one=>"uno"}, {:two=>"dos(2)"}] > irb(main):005:0> TAB_SET > => [{:one=>"uno"}, {:two=>"dos(2)"}] > > I believe the problem is NOT that the array is a reference. Using > either "new" or "dup" should get me a fresh array. The problem is that > the array ELEMENTS (the hashes) are references back to the original > hashes. So I''m guessing I need something like: > > tabs = Array.new( Hash.new(hash1), Hash.new(hash2), ... ) > But that sure isn''t very pretty! > > Anybody know an easier way? > > sjf--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Rein Thanks for sticking with this subject. I am sure I am missing your point here. The problem exists whether the code is in a separate class or not. As a test I added the file tab_set.rb to my models. It contains... class TabSet TAB_SET = [{:label=>"one"}, {:label=>"two"}] def initialize @a = Array.new(TAB_SET) end def process @a[1][:label] = TAB_SET[1][:label] + "(2)" # grab the original value and append a string @a end end I then run it from the console...>> a = TabSet.new=> #<TabSet:0x36caf40 @a=[{:label=>"one"}, {:label=>"two"}]>>> b = a.process=> [{:label=>"one"}, {:label=>"two(2)"}]>> b = a.process=> [{:label=>"one"}, {:label=>"two(2)(2)"}] in fact, its even worse -- since if I now try to NEW another tabset...>> c=TabSet.new=> #<TabSet:0x36c7048 @a=[{:label=>"one"}, {:label=>"two(2)(2)"}]> What did I miss? Thanks, sjf On Sep 3, 1:29 pm, Rein Henrichs <rein.henri...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> In a situation like this, where you are using basic data structures > and defining all this business logic external to them to control their > use, the ruby way would be to encapsulate all this logic in a class > and use it like: > > @tabs = TabSet.new( options ) > > This way your TabSet class encapsulates all the logic of initializing > and controlling a set of tabs (whatever that may be). You can use > intention-revealing methods on that class and its instances to achieve > its goals. >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
The issue that you are running into here is one of scope. Since TAB_SET is the same across all instances of the class, modifying it will modify all variables that point to it. The point of using a class is that you can change the implementation to be more robust, not just to wrap your current implementation inside a class. On Sep 3, 8:28 pm, sjf_control <sjfcont...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Rein > > Thanks for sticking with this subject. > I am sure I am missing your point here. The problem exists whether the > code is in a separate class or not. As a test I added the file > tab_set.rb to my models. It contains... > > class TabSet > > TAB_SET = [{:label=>"one"}, {:label=>"two"}] > > def initialize > @a = Array.new(TAB_SET) > end > > def process > @a[1][:label] = TAB_SET[1][:label] + "(2)" # grab the original > value and append a string > @a > end > end > > I then run it from the console... > > >> a = TabSet.new > > => #<TabSet:0x36caf40 @a=[{:label=>"one"}, {:label=>"two"}]>>> b = a.process > > => [{:label=>"one"}, {:label=>"two(2)"}]>> b = a.process > > => [{:label=>"one"}, {:label=>"two(2)(2)"}] > > in fact, its even worse -- since if I now try to NEW another tabset... > > >> c=TabSet.new > > => #<TabSet:0x36c7048 @a=[{:label=>"one"}, {:label=>"two(2)(2)"}]> > > What did I miss? > > Thanks, > sjf > > On Sep 3, 1:29 pm, Rein Henrichs <rein.henri...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > > In a situation like this, where you are using basic data structures > > and defining all this business logic external to them to control their > > use, the ruby way would be to encapsulate all this logic in a class > > and use it like: > > > @tabs = TabSet.new( options ) > > > This way your TabSet class encapsulates all the logic of initializing > > and controlling a set of tabs (whatever that may be). You can use > > intention-revealing methods on that class and its instances to achieve > > its goals.--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
This isn''t that pretty either but you could possibly do: tabs = TAB_SET.dup.map(&:dup) --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---