0

I need to create a temporary array containing model objects to manipulate. I need the auto_increment primary key but I do not want the associations kept because when I remove the object from the array it removes it from the database as well (thanks rails, pretty inconvenient). The deep copies I create using object.dup have the id's marked as nil. How do I keep them in the copy?

user58446
  • 269
  • 1
  • 3
  • 17
  • _"thanks rails, pretty inconvenient"_ my interest in helping is waning fast. http://xyproblem.info/ – matthewd Aug 19 '18 at 10:52
  • You sound like you wrote the framework. Did you? – user58446 Aug 19 '18 at 10:54
  • I've been through a number of possible solutions and a number of hours troubleshooting, The xy problem sounds difficult to prevent, since I would just start trying to solve X if I didn't have reason to be believe it might be Y. – user58446 Aug 19 '18 at 10:58
  • I'm on the core team, yes. You want to call `to_a` on the relation, not copy the individual records. (If you do want to copy the records, you want `clone`. But you really don't need to do that.) – matthewd Aug 19 '18 at 10:58
  • Fine then, your waning interest is warranted. Call it frustration from my own not following all of the conventions and trying to configure. Thanks for your help, I'll try to_a. – user58446 Aug 19 '18 at 11:02
  • The question is **how** are you trying to remove the object from the collection? Is your collection an `ActiveRecord::Relation`, an `Array` or something else? – 3limin4t0r Aug 19 '18 at 11:07
  • Ultimately, I still think either [`reject`](https://stackoverflow.com/a/51916360/476979) or [a subquery](https://stackoverflow.com/a/51916880/476979) is a better solution to the actual "I want a list of records that excludes some of them" problem (the X to this question's Y) – matthewd Aug 19 '18 at 11:21
  • The collection is an array now that I am running .to_a on it and the id's are now preserved. I am trying to remove the object via whatever method works delete, delete_at, reject!, I've tried many. Something is wrong with my looping operation because I wind up with the wrong elements remaining in the end. Done this plenty of times in other languages, removing items from reversed lists, and this is just a nested loop search... So I really can't figure out what's wrong (aside from the id's being nil). Going on 10 hours now. Now I just feel tired _and_ stupid. – user58446 Aug 19 '18 at 11:26
  • Ask _that_ question: `delete` & friends are much too low-level... you're making it harder than it needs to be (and yes, the more low-level you go, the easier it is to get trapped among operations that do something other than what you expect). – matthewd Aug 19 '18 at 11:31
  • Pardon my blindness, which question are you suggesting I ask? A rewording of the comment above regarding the malformed nested loops? Looking into a subquery solution now. – user58446 Aug 19 '18 at 11:35
  • "I am trying to [exclude some objects from a collection] via whatever method works, I've tried [delete, ..]" -- move up one level of detail from "here's how my `delete` is misbehaving, what's wrong?" to "here's what I need to achieve, and how my current approach is failing, what should I do differently?". – matthewd Aug 19 '18 at 11:43
  • I'm not sure which other languages you're familiar with for a good analogy, but: solving this in Ruby with an explicit nested loop is a bit like doing it in C using only `if` and `goto`: you _can_, but the higher level `for` is easier, less error prone, and more idiomatic. Ruby just has more "higher levels" built in to the language core. – matthewd Aug 19 '18 at 11:51

1 Answers1

2

dup describes this behaviour:

Duped objects have no id assigned and are treated as new records.

The dup method does not preserve the timestamps (created|updated)_(at|on).

It does that because people almost always want to duplicate the record in order to save a new copy of it to the database.

If you need a truly identical copy, clone will do that, but it's almost never what you need: you almost certainly just want another reference to the same object.


In this case, from context and other comments, I understand that your goal is a list of records, such that you can delete entries and not have it affect the database.

For that, you need only to_a. It sounds like you currently have an ActiveRecord::Associations::CollectionProxy (or if not, an ActiveRecord::Relation), which acts like an array, but represents the associated set in the database: what manipulations it allows, will be immediately reflected in the database. to_a will give you an ordinary Ruby Array representing that list; you can then manipulate that array however you like. (Per the first half of this answer, this is a situation where you do not need to copy the individual records... you just need them in a simple array.)

3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
matthewd
  • 4,332
  • 15
  • 21
  • can you add the part about the effect of .to_a (other than converting to an array so I understand a little more) and I'll mark it as answered. thanks. – user58446 Aug 19 '18 at 11:28