Problem with copying array

J

jbieger

Hi, I'm a total newbie with Ruby and I was trying to make this
function that would get a (multidimensional) array, make a copy, make
some changes to the copy and then return the copy, without altering
the original array. However, no matter what I try, the original array
gets altered. Here is my code:

def prune_times (times)
# copy = times
# copy = Array.new(times)
# copy = times.clone
copy = times.dup
copy.each do |map|
map.each do |task|
task.replace(best_n(task, 2))
end
end
return copy
end

Any help would be greatly appreciated!

Jordi
 
B

Bryan Duxbury

What's your array an array of? Array.dup will copy the array to a new
array, but if the values in the array are references to some class
you're using, it'll be a different array of the same items.
Primitives would be copied correctly. You need to do your own deep
copy if it's a more complicated array value.

-Bryan
 
J

jbieger

Thanks for your reply!

Like I said, it's a multidimensional array, so I suppose that the
contents aren't primitive and I need to do as you suggested. Is there
a built-in function for this, or do I need to make my own. It seems
like a pretty common operation...

Jordi
 
F

Frederick Cheung

Thanks for your reply!

Like I said, it's a multidimensional array, so I suppose that the
contents aren't primitive and I need to do as you suggested. Is there
a built-in function for this, or do I need to make my own. It seems
like a pretty common operation...

You need to do it yourself. It may be pretty common, but the exact
specifics of how deep to go etc... tend to be rather application
dependant.

Fred
 
M

Matthew Harris

Thanks for your reply!

Like I said, it's a multidimensional array, so I suppose that the
contents aren't primitive and I need to do as you suggested. Is there
a built-in function for this, or do I need to make my own. It seems
like a pretty common operation...

Jordi
Your methods of copying the array are correct (though, you should remove
one and only use the other, which ever). The problem is that with some
objects, Ruby doesn't know how to "copy" them.

Bryan already mentioned this, but for primitives like strings, integers,
floats and whatnot, Ruby knows how to copy these objects, but if you
have custom classes or other classes that don't define their own
behavior for copying, then Ruby will keep the reference.

Hope that helps.
 
R

Ryan Davis

Hi, I'm a total newbie with Ruby and I was trying to make this
function that would get a (multidimensional) array, make a copy, make
some changes to the copy and then return the copy, without altering
the original array. However, no matter what I try, the original array
gets altered. Here is my code:

`ri Enumerable#map`
 
S

Sebastian Hungerecker

Matthew said:
Bryan already mentioned this, but for primitives like strings, integers,
floats and whatnot, Ruby knows how to copy these objects

Strings can be copied with the dup method, yes. But if you mean to imply that
that happens automatically when dupping an array of Strings, you are wrong:
arr=["chunky", "bacon"]
arr.dup.each {|str| str.tr!("ck","kc")}
arr
=> ["khuncy", "bakon"]

Integers and Floats can not be copied at all:TypeError: can't dup Fixnum
TypeError: allocator undefined for Float
but if you
have custom classes or other classes that don't define their own
behavior for copying, then Ruby will keep the reference.

If you dup an array you will always get a new array with references to the old
objects. dup never makes a deep copy.
Presumably there is no built-in way to make a deep copy because, as mentioned
above, some objects (like Integers) can't be copied.


I hope this cleared the confusion a bit,
Sebastian
 
S

Sebastian Hungerecker

Bryan said:
What's your array an array of? Array.dup will copy the array to a new
array, but if the values in the array are references to some class
you're using, it'll be a different array of the same items.
Primitives would be copied correctly. You need to do your own deep
copy if it's a more complicated array value.

a) There are no primitives in ruby. Everything is an object.
b) Array#dup never makes a deep copy. You'll always get a different array of
the same items no matter what the class of these items is.


HTH,
Sebastian
 
J

Jan Dvorak

Hi, I'm a total newbie with Ruby and I was trying to make this
function that would get a (multidimensional) array, make a copy, make
some changes to the copy and then return the copy, without altering
the original array. However, no matter what I try, the original array
gets altered. Here is my code:

Another possibility is serialization - it's still your job to make your
classes serializable and there is stuff that cannot be serialized, but for
simple cases (multi-dimensional arrays) it works out-of-box:

irb(main):001:0> a = [["a","b"],[1,2]]
=> [["a", "b"], [1, 2]]
irb(main):002:0> b = Marshal.load(Marshal.dump(a))
=> [["a", "b"], [1, 2]]
irb(main):003:0> b[0][0], b[1][0] = "x", 9
=> ["x", 9]
irb(main):004:0> a
=> [["a", "b"], [1, 2]]
irb(main):005:0> b
=> [["x", "b"], [9, 2]]

Jan
 
A

Alan Slater

Highly related newbie question, please pardon the non-technical
language.

So, arrays (even arrays of 'primitives' like strings) don't actually
contain the objects that fill each entry, they instead (effectively)
link to those objects. So, .delete_at and .slice! (for example) don't
remove things from the array, they delete the objects that are being
linked to completely.

This explains my problem (why, whenever I delete an entry from an array,
it also deletes it from every copy of the array).

So, how could I simply remove an object from an array, without deleting
the object itself? Something like, array_name.remove_at(x) (except
obviously that doesn't exist).

I've been through the API and can't find anything that seems to fit. I
tried array_name[x] = nil, but that just left an empty entry and didn't
change the length of the array. Even that seems to change the original
object to nil too, as following array_name[x] = nil with
array.delete_at(x) removes the equivalent entry from the original array
as well.

How can I remove an entry from an array without deleting the object that
the entry refers to?
 
D

David A. Black

Hi --

Highly related newbie question, please pardon the non-technical
language.

So, arrays (even arrays of 'primitives' like strings) don't actually
contain the objects that fill each entry, they instead (effectively)
link to those objects. So, .delete_at and .slice! (for example) don't
remove things from the array, they delete the objects that are being
linked to completely.

This explains my problem (why, whenever I delete an entry from an array,
it also deletes it from every copy of the array).

So, how could I simply remove an object from an array, without deleting
the object itself? Something like, array_name.remove_at(x) (except
obviously that doesn't exist).

I've been through the API and can't find anything that seems to fit. I
tried array_name[x] = nil, but that just left an empty entry and didn't
change the length of the array. Even that seems to change the original
object to nil too, as following array_name[x] = nil with
array.delete_at(x) removes the equivalent entry from the original array
as well.

How can I remove an entry from an array without deleting the object that
the entry refers to?

Removing an entry from an array never destroys the object.


s = "string"
a =
a.clear
p a # []
p s # "string"

As long as a reference to the object exists, the object will still be
available. Objects don't even know whether or not they are in
collections.


David

--
Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
Advancing with Rails January 19-22 Fort Lauderdale, FL *
* Co-taught with Patrick Ewing!
See http://www.rubypal.com for details and updates!
 
B

Brian Candler

Alan said:
arrays (even arrays of 'primitives' like strings) don't actually
contain the objects that fill each entry, they instead (effectively)
link to those objects.

Yes - they contain references to objects.
So, .delete_at and .slice! (for example) don't
remove things from the array, they delete the objects that are being
linked to completely.

No - they delete the references from the array.

The objects themselves are unchanged. If there are other references to
them elsewhere in the system, they will live on. If the array element
was the only reference to the object, so that there are no more now,
then at some point in the future the object will be garbage-collected.
This explains my problem (why, whenever I delete an entry from an array,
it also deletes it from every copy of the array).

No - you are deleting from a single array, but you have multiple
references to that array from other places, so they all see the same
changed array.

A good way to probe this is with the 'object_id' method.

irb(main):001:0> a = [[1,2,3],[4,5,6]]
=> [[1, 2, 3], [4, 5, 6]]
irb(main):002:0> a.object_id
=> -605380508
irb(main):003:0> a[0].object_id
=> -605380598
irb(main):004:0> a[1].object_id
=> -605380608
irb(main):005:0> b = a.dup
=> [[1, 2, 3], [4, 5, 6]]
irb(main):006:0> b.object_id
=> -605417698
irb(main):007:0> b[0].object_id
=> -605380598
irb(main):008:0> b[1].object_id
=> -605380608

You can see that a and b are different objects, but a[0] and b[0] point
to the same object (ditto a[1] and b[1]). Diagramatically:

a -----> [ . , . ]
| |
v v
[1,2,3] [4,5,6]
^ ^
| |
b -----> [ . , . ]

The sub-arrays don't belong to 'a' any more than they belong to 'b'. So
the consequence is:

irb(main):009:0> a[0] << 99
=> [1, 2, 3, 99]
irb(main):010:0> a
=> [[1, 2, 3, 99], [4, 5, 6]]
irb(main):011:0> b
=> [[1, 2, 3, 99], [4, 5, 6]]

However if you make a modification to one of the outer arrays, e.g. by
adding or removing an element, this is fine:

irb(main):012:0> a << [7,8,9]
=> [[1, 2, 3, 99], [4, 5, 6], [7, 8, 9]]
irb(main):013:0> a
=> [[1, 2, 3, 99], [4, 5, 6], [7, 8, 9]]
irb(main):014:0> b
=> [[1, 2, 3, 99], [4, 5, 6]]
So, how could I simply remove an object from an array, without deleting
the object itself?

slice! or delete_at

Regards,

Brian.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top