I'm attempting to create something of a tree structure.
I wish to access my data as follows:
data[key1][key2]
However, key1 and key2 have a symmetric relationship; the following is always true:
data[key1][key2] == data[key2][key1]
More specifically, I have data[key1][key2]
and data[key2][key1]
pointing to the same object, such that changes to one affect the other.
My problem arises because I wish to delete the underlying object. I know if I use:
delete data[key1][key2];
data[key2][key1]
still refers to the object.
My question is this: is there any way to remove the underlying object, or overwrite it with something falsey, such that both properties above will evaluate falsey?
3 Answers 3
Think of it like this:
data[key1][key2] -----> object1
^
data[key2][key1] ---------+
If you change the key for one of them (say the latter) to a new object you'll just get this:
data[key1][key2] -----> object1
data[key2][key1] -----------> object2
(Or the key will simply be missing if you delete it:)
data[key1][key2] -----> object1
data[key2]
That is, you'll change one of the references but not the other. Any attempt to replace only one of them will accomplish this. You have two options:
a) Change both keys to point to the new object:
data[key1][key2] -----> object2
^
data[key2][key1] ---------+
(Or delete both keys:)
data[key1][key2] object1 (no references, will be garbage collected)
data[key2][key1]
b) Modify a field on the object itself. This way they'll both still be pointing to the same object, but something about it will be different. Any code that you use will have to take this into account, though.
data[key1][key2] -----> object1 with 'isDeleted'=true
^
data[key2][key1] ---------+
The a) option seems to make the most sense given your use case. If you have both keys anyway, why not update/delete both?
9 Comments
If you have both keys anyway, why not update both?
I'm just trying to learn something. Thank you for your response.void*
as your object or as the 'falsey' value. the equivalent here would be to add a 'deleted' property to the object - both require you to change your code to accomodate that decision. oh and no worries about "just trying to learn something", that's how we get better at stuff =). EDIT: yea you can't free the data in JS but you can modify it with the 'deleted' approach, it seems to me the 2 are analogousThe valueOf() method might offer a solution. If you're doing an explicit comparison to a boolean value, valueOf will be called as part of the type-casting to Boolean. For example, let's say your data structure references some object, x
...
> x = {}
Object
> (x == false ? 'yes' : 'no')
"no"
> x.valueOf = function() {return false;}
function () {return false;}
> (x == false ? 'yes' : 'no')
"yes"
... i.e. if data[key1][key2] == data[key2][key1] == x
, then assigning x.valueOf = function() {return false;}
will change what x
evaluates to when explicitely caste to a boolean. So as long as you're testing for data[key1][key2] == false
, x
should appear to be falsy.
However, you need to be careful with this because x
is less falsy than a primitive value. E.g. Even after assigning valueOf as above, implicit coercions still appear to be truthy ...
(x ? 'yes' : 'no')
"yes"
2 Comments
valueOf
is not a good idea as it's only used in certain expressions involving comparison algorithms, not in type conversions. E.g. Boolean(x)
and !!x
will both return true
because valueOf
isn't used for type conversion.You've probably got what you need from other the answers, but to summarise:
My question is this: is there any way to remove the underlying object
No. You can remove all references to it to make it available for garbage collection. When the garbage collector runs (at some unspecified time), it will remove the object.
or overwrite it with something falsey, such that both properties above will evaluate falsey?
Not exactly. You can only assign a new value to each property so that they evaluate to false (false, null, undefined, 0, etc.). Objects don't keep track of the identifiers that reference them, so there is no general way to say "remove all reference to this object". If you want to do that, you'll have to keep track of references and change their values yourself.
An alternative is to give the object a property to indicate whether it should be used or not (e.g. "obsolete"), that can be checked when accessing other properties. A public property is pretty simple, or you can create a "private" member using a closure in the constructor:
function Foo() {
var obsolete = false;
this.obsolete = function (){
return obsolete;
};
this.delete = function (){
obsolete = true;
};
}
var foo = new Foo();
var bar = new Foo();
alert(foo.obsolete() + ', ' + bar.obsolete()); // false, false
foo.delete();
alert(foo.obsolete() + ', ' + bar.obsolete()); // true, false
That will work much the same as you want. If you could delete objects you'd need to do:
if ( data[key1][key2] ) {
// do stuff
}
Using the above method you'd do:
if ( !data[key1][key2].obsolete() ) {
// do stuff
}
Noting that the object still actually exists. You may want to change obsolete to current and reverse the boolean so you check if (obj.current())
rather than if (!obj.obsolete())
. Whatever suits.
isDeleted
property to the object, or wrapping the structure in a "manager", which controls the addition, retrieval and deletion of these elements.data[key1][key2]
anddata[key2][key1]
always refer to the same object, why bother with two references at all? You could have one reference, and sort the keys alphabetically in a method you call to retrieve a value.