Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.javascript > #31918
| From | Thomas 'PointedEars' Lahn <PointedEars@web.de> |
|---|---|
| Newsgroups | comp.lang.javascript |
| Subject | Re: Get object from one of its values |
| Date | 2016-12-20 13:33 +0100 |
| Organization | PointedEars Software (PES) |
| Message-ID | <2298053.9Mp67QZiUf@PointedEars.de> (permalink) |
| References | <W9ednbBeYoFENcXFnZ2dnUU7-RfNnZ2d@westnet.com.au> |
Andrew Poulos wrote:
> If there are one or more objects each of which has a property called
> childArray whose value is always a one dimensional array and elements of
> the array are unique (ie an element cannot also occur in the childArray
> of another object) then if I know the value of one element of a
> childArray how do I find the object it's associated with?
>
> For example if I have:
>
> var a = {};
> a.childArray = [1,2,3,a,b,c,..];
> var b = {};
> b.childArray = [4,5,6,d,e,f,...];
> var c = {};
> c.childArray = [7,8,9,g,h,i,...];
> ...
>
> and I'm given the value "5" how do I find that it's associated with "b"?
>
> The code is dynamically created so I don't know beforehand what or how
> many objects will be created.
As Jake said correctly, you need to check the objects sequentially (but see
bottom). This means that you need to check the variables or properties
sequentially that hold references to those objects.
If you know the variables or properties, you can create an array of their
values. With ECMAScript 2016 methods and syntax:
var objects = [a, b, c];
objects.find(obj =>
obj.hasOwnProperty("childArray") && obj.childArray.indexOf(5) > -1);
It might also be necessary to check whether obj.childArray holds a reference
to an Array instance, using Array.isArray().
But suppose the less trivial case that there are variables “a” to at most
“z” to refer to objects, and you do not know how many, then you will have to
use a loop that is checking whether the variable exists and its value is an
object reference. For example:
var obj;
for (var i = "a".charCodeAt(0), end = "z".charCodeAt(0);
i <= end; ++i)
{
var name = String.fromCharCode(i);
/*
* NOTE:
* It is not possible to write a function that determines if a
* variable was declared in a specified execution context as
* there is no way to refer to a calling execution context.
* It is only possible to define such a function in the execution
* context that is to be searched. (this != [[Scope]])
*/
try
{
obj = eval(name);
}
catch (e)
{
if (e instanceof ReferenceError) continue;
}
if (typeof obj != "object" || obj == null) continue;
if (!obj.hasOwnProperty("childArray") || obj.childArray.indexOf(5) < 0)
{
obj = null;
}
else
{
break;
}
}
if (typeof obj == "object" && obj != null)
{
/* use obj */
}
This can be written in a slightly shorter way using an array of identifiers:
/* ["a", "b", "c", …, "z"]; a useful pattern to remember */
var identifiers = Array.apply(null, {length: 26}).map(
(() => {
var offset = "a".charCodeAt(0);
return ((e, i) => String.fromCharCode(i + offset));
})()
);
var matchingIdentifier = identifiers.find(name => {
var obj;
try
{
obj = eval(name);
}
catch (e)
{
if (e instanceof ReferenceError) return false;
}
if (typeof obj != "object" || obj == null) return false;
if (!obj.hasOwnProperty("childArray") || obj.childArray.indexOf(5) < 0)
{
return false;
}
return true;
});
if (typeof matchingIdentifier == "string")
{
var obj = eval(matchingIdentifier);
}
With variables, you can only avoid eval() if you declare potentially missing
variables in advance:
var a, b, …, z;
Then you can generate the array containing the values to be searched as
follows:
var objects = [a, b, …, z];
And proceed as if you knew how many variables there are in the original
code.
The caveat here is that you must not use variable names that are already
used by the original code. The only way to be certain that this does not
happen is declaring variables with “let” (ECMAScript 2015+) instead of “var”
within a Block-like statement of your code, and then only the missing ones.
If possible, access properties instead of variables in this situation; then
you can use Object.property.hasOwnProperty(), or simply “"propertyName" in
object” if inheritance is not an issue.
Finally, if you need to do this search several times, and the referred
objects are not changing in-between, it will improve efficiency greatly if
you maintain a map of values to objects instead:
var v2o = new Map();
objects.forEach(obj => obj.childArray.forEach(el => v2o.set(el, obj)));
Determining the object whose “childArray” property refers to an Array
instance that contains the Number value “5” will then be as simple as
var needle = v2o.get(5);
whereas “needle” will either hold a reference to that object, or “undefined”
if there is no matching object in the indexed haystack referred to by “v2o”.
[Note that this way a value will map to a reference to the last searched
object in which it was found. It is also possible to map a value either to
an array of references to matching objects, or to a reference to the first
searched matching object.
Note also that to know the identifier/name of the variable(s) or
property/properties that referred to that object you need to keep an array
(also) of names, not (only) object references. Objects have identity, not
name.]
The Map object is specified as a constructor property of the global object
since ECMAScript 2015 as well (ES 2015, § 18.3.4) A simplified polyfill of
it is easily written:
if (typeof Map != "function")
{
var Map = function () {
/*
* NOTE:
* Assigns reference to object with empty prototype chain,
* to avoid name collisions; a polyfill may be necessary.
*/
this.items = Object.create(null);
};
Map.prototype.get = function (key) {
return this.items[key];
};
Map.prototype.set = function (key, value) {
this.items[key] = value;
};
}
For a more elaborate version, that is however not yet written to be
ECMAScript 2015 compliant as it was based on the Java implementation (e.g.,
.put = .set), see
<https://github.com/PointedEars/JSX/blob/517e2fa56831a3dff8eb2bb7c7e7cbe45c5d0a86/map.js>.
--
PointedEars
FAQ: <http://PointedEars.de/faq> | <http://PointedEars.de/es-matrix>
<https://github.com/PointedEars> | <http://PointedEars.de/wsvn/>
Twitter: @PointedEars2 | Please do not cc me./Bitte keine Kopien per E-Mail.
Back to comp.lang.javascript | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread
Get object from one of its values Andrew Poulos <ap_prog@hotmail.com> - 2016-12-20 14:43 +1100
Re: Get object from one of its values Jake Jarvis <pig_in_shoes@yahoo.com> - 2016-12-20 10:42 +0100
Re: Get object from one of its values Andrew Poulos <ap_prog@hotmail.com> - 2016-12-21 07:25 +1100
Re: Get object from one of its values Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2016-12-20 13:33 +0100
Re: Get object from one of its values Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2016-12-20 23:10 +0100
Re: Get object from one of its values John Harris <niam@jghnorth.org.uk.invalid> - 2016-12-20 18:21 +0000
csiph-web