Path: csiph.com!weretis.net!feeder4.news.weretis.net!news.mb-net.net!open-news-network.org!.POSTED.39.234.197.178.dynamic.wless.lssmb00p-cgnat.res.cust.swisscom.ch!not-for-mail From: Thomas 'PointedEars' Lahn Newsgroups: comp.lang.javascript Subject: Re: Recording changes to an object Date: Thu, 21 Jun 2018 21:03:38 +0200 Organization: PointedEars Software (PES) Lines: 174 Message-ID: <1620852.VI2in3QvqR@PointedEars.de> References: Reply-To: Thomas 'PointedEars' Lahn Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8Bit Injection-Info: gwaiyur.mb-net.net; posting-host="39.234.197.178.dynamic.wless.lssmb00p-cgnat.res.cust.swisscom.ch:178.197.234.39"; logging-data="2911"; mail-complaints-to="abuse@open-news-network.org" User-Agent: KNode/4.14.2 Cancel-Lock: sha1:+CKUpdxD6eXLUeVCanzQ88uV6Kc= Face: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEXTxa4RFk5dUWANED8PFEfy7+MGBiW+n3ZNF/QuAAACaElEQVQ4jVXUwVOcMBQG8Dc7Rc4PUntdWV2uxjDpGaGeozOp1woar4jd5t/v9wLstMwsA/ntlxdCAgUc1hjTc9/JCZfGoo3wG3HdmdAWrIJRHe7GM/TmpY5VFefuVcAkkPbLIaN8rmPmjloyZxgyR3GuJ4K0AGtJ2htz8o7yqikm759fldQXaMpbDzjKAG+8v+AugVTOPO5DOjLvGtUYQwh0CPjnVMyGd+8/GfUB5nLKJDD2aLDh5HYyMDJGDwQIo2ZmZcKbowNmAdB/AzyFhrmF2MHRb0QJJfaAnwGB6orZhoykLzJtGwF/xpYxI1dswomiUj3gTuAIqCn/4C7cULwGNBtwMTk3Y4LfKB5YUaOKBKYtpplm7u0vip8tU1NWWyI/7XdcSuIDoMt6rVHMWT0DbjHPGqDqZVSa6zleLcUTcIKLoMv3ueJluALtAo9B302zPPlrtiVScRdCjXvVh3e3JpYa/jjkuC9N+LrBMlz/eAN4eQijX2EdLo6c5tGGHwLyHFtXk89dDGHwCVhG9T0S/j55AhRZgkMCmUQXJ49TnS1wnQDvw0eAh9ICeMmEFbCnPMFzjAvsWoEWEFdYEx+S0MoUZ1gT1wId8+AF3Bl2OoEu906AUHx5VLw/gXYg/x84loOah/2UYNrgiwSwGO7RfUzVBbx/kgpckumGOi6QirtD6gkLTitbnxNol47S2jVc2vsN5kPqaAHT8uUdAJM4v/DanjYOwmUjWznGfwB7sGtAtor5BgofDuzaRj4kSQAqDakTsKORa3Q3xKi3gE1fhl71KRMqrdZ2AWNNg/YOhQyrVBnb+i+nEg4bsDA+egAAAABJRU5ErkJggg== X-User-ID: U2FsdGVkX1/QiBIA9A3L6HfY0zI46ABB3EEDrqoeE+Dr3Jtv61Bj+w== X-Face: %i>XG-yXR'\"2P/C_aO%~;2o~?g0pPKmbOw^=NT`tprDEf++D.m7"}HW6.#=U:?2GGctkL,f89@H46O$ASoW&?s}.k+&. If I have a some what large object (ie an object with lots of properties > that looks like: > > var obj = { > "1": {"a":1, > "b":"data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678", "c":3, > ...}, > "2": {"a":2, ... > "3": ... > ... > }; > > You cannot add or delete any property from a top level value eg within > the object {"a":1, "b"... ) > All the top level values have the same properties. > Top level properties may be added or deleted. > > Is their some approach I can follow to make a multiple level undo? Yes. > I started by making copies of the object each time it was changed but > within minutes there was a huge number of copies. I thought about > limiting the undo level to, say, 8 levels but if you set a property > value to the value it currently is (ie. you don't actually change the > value) it was still recording it as a change - so trying to do an undo > did "nothing". Yes, that is a terrible idea in the first place. > I need something that does: > > 1. Has the object has been changed? > 2. If, yes, what was the change eg obj['1'].a value changed from 1 to 2 > 3. When the user calls undo it finds the change and reverses it. Property changes should only be possible via setter. You can either make the actual properties private and have a getter that returns their actual value – var o = Object.create(null, (function () { var _foo; return { foo: { "get": function () { return _foo; }, "set": function (value) { // … _foo = value; // … } } }; }())); –, or you can use a Proxy: var o = new Proxy(Object.create(null), { "get": function (target, name) { return target[name]; }, "set": function (target, name, value) { // … target[name] = value; // … }, deleteProperty: function (target, prop) { // … var success = delete target[prop]; // … return success; } }); [I am deliberately quoting “get” and “set” here because they had been reserved words in at least one implementation once.] Then in the setter/trap you can update the item of a Map, where you use the object reference as the key, with the pertinent information: var all_changes = new Map(); var o = {}; var changes = all_changes.get(o); var change = Object.assign(Object.create(null), {…}); changes ? changes.push(change) : all_changes.set(o, [change]); If you cannot use native Map, you can use my jsx.map.Map or something similar. If you cannot use that too, you can find some way to determine a path of an object to be modified as a string and use that as the key for an object instance (that is what jsx.map.Map does if the key is a reference to an object). As an alternative you can use special, otherwise unused, properties in the same object to record the changes. If so, it will be good if the prototype chain of the target object is empty. You could also use my jsx.object.findNewProperty() to determine an unused property name. An Array of objects will be useful for the undo list. Proof of concept: var UNDO_ADD = 1; var UNDO_MODIFY = 2; var UNDO_DELETE = 3; function setProperty (obj, name, newValue) { var undo_record = Object.create(null); var old_value = obj[name]; if (!(name in obj)) { Object.assign(undo_record, { action: UNDO_ADD, name: name, value: newValue }); } else if ((old_value = obj[name]) !== newValue) { Object.assign(undo_record, { action: UNDO_MODIFY, name: name, oldValue: old_value, newValue: newValue }); } obj[name] = newValue; (obj._changes || (obj._changes = [])).push(undo_record); } function deleteProperty (obj, name) { if (name in obj) { var value = obj[name]; if (delete obj[name]) { var undo_record = Object.assign(Object.create(null), { action: UNDO_DELETE, name: name, value: value }); (obj._changes || (obj._changes = [])).push(undo_record); } } } var obj = Object.create(null); setProperty(obj, "foo", "bar"); setProperty(obj, "foo", "baz"); deleteProperty(obj, "foo"); obj._changes Implementation of the rollback is left as an exercise to the reader. Do not use Object.prototype.watch() as it is proprietary and deprecated: -- PointedEars FAQ: | | Twitter: @PointedEars2 | Please do not cc me./Bitte keine Kopien per E-Mail.