I have a function that receives a list of JS objects as an argument. I need to store information about those objects in a private variable for future reference. I do not want to stuff a property into the objects themselves, I just want to keep it out of band in a dictionary. I need to be able to lookup metadata for an object in sub-linear time.
For this I need a hash function such that, for any two objects o1 and o2,
hash(o1) !== hash(o2) whenever o1 !== o2.
A perfect example of such a hash function would be the memory address of the object, but I don't think JS exposes that. Is there a way?
Each object reference is different. Why not push the object onto an array? Traversing the array looking for an object reference might still perform better than inspecting each object in a recursive manor to generate a hash key.
function Dictionary() {
var values = [];
function contains(x) {
var i = values.length;
while(i--) {
if (values[i] === x) {
return true;
}
}
return false;
}
function count() {
return values.length;
}
function get(i) {
return (i >= 0 && i < values.length) ? values[i] : null;
}
function set(o) {
if (contains(o)) {
throw new Error("Object already exists in the Dictionary");
}
else {
return values.push(o) - 1;
}
}
function forEach(callback, context) {
for (var i = 0, length = values.length; i < length; i++) {
if (callback.call(context, values[i], i, values) === false) {
break;
}
}
}
return {
get: get,
set: set,
contains: contains,
forEach: forEach,
count: count
};
}
And to use it:
var objects = Dictionary();
var key = objects.set({});
var o = objects.get(key);
objects.contains(key); // returns true
objects.forEach(function(obj, key, values) {
// do stuff
}, this);
objects.count(); // returns 1
objects.set(o); // throws an error
To store metadata about objects, you can use an WeakMap:
WeakMaps are key/value maps in which keys are objects.
Note that this API is still experimental and thus not widely supported yet (see support table). There is a polyfill implementation which makes use of defineProperty to set GUIDs (see details here).
Javascript does not provide direct access to memory (or to the file system for that matter).
You'd probably just want to create your properties/variables within the analysis (hash) function, and then return them to where the function was called from to be stored/persisted for later reference.
Thanks everyone who chipped in to reply. You all have convinced me that what I want to do is currently not possible in JavaScript.
There seem to be two basic compromises that someone with this use case can chose between:
Linear search using ===
=== appears to be the only built-in way to distinguish between two identically-valued objects that have different references. (If you had two objects, o1 and o2, and did a deep comparison and discovered that they were value-identical, you might still want to know if they're reference-identical. Besides === you could do something weird like add a property to o1 and see if showed up in o2).
Add a property to the object.
I didn't like this approach because there's no good reason why I should have to expose this information to the outside world. However, a colleague tipped me off to a feature that I didn't know about: Object.defineProperty. With this, I can alleviate my main concerns: first, that my id would show up, unwanted, during object enumeration, and second, that someone could inadvertently alter my id if there were to be a namespace collision.
So, in case anyone comes here wanting the same thing I wanted, I'm putting it up there for the record that I'm going to add a unique id using Object.defineProperty.
Related
I'm working on some algorithms that require me to manipulate data inside a stack, they gave me a class where they defined 3 methods
.pop() which pops the top value of the stack
.push() which pushes a value on to the stack
.peek() which shows me the top value of the stack without modifying the stack
but i don't understand what the difference would be of using the methods in this class versus using the already built in JavaScript methods of push() and pop(), and only having to define .peek() in this class
below is the class stack they gave me as well as an algorithm example used to count the values in the stack, if I am missing any information please let me know and I will update the question to try and make it more clear
class Stack {
constructor(...values) {
const data = {};
let index = 0;
this.push = function(value) {
if (arguments.length < 1) {
throw new TypeError('stack.push() requires a value argument');
}
if (typeof value === 'undefined') {
throw new TypeError('stack.push(value) received undefined');
}
data[index] = value;
index++;
};
this.pop = function() {
const last = index - 1;
if (last < 0) return;
const value = data[last];
delete data[last];
index = last;
return value;
};
this.peek = function() {
const last = index - 1;
if (last < 0) return;
return data[last];
};
this.print = function() {
if (index === 0) {
return 'Stack { <empty> }';
}
let output = ' }';
let last = index - 1;
for (; last > 0; last--) {
output = ' <- ' + JSON.stringify(data[last]) + output;
}
output = JSON.stringify(data[last]) + output;
return 'Stack { ' + output;
};
for (let i = 0; i < values.length; i++) {
this.push(values[i]);
}
Object.freeze(this);
}
}
function countValues(stack) {
let count = 0;
while (true) {
if (stack.pop() === undefined) {
break;
}
count++;
}
return count;
}
The reason for defining your own class is for abstraction purposes. Even though Array has push() and pop() methods, it's not actually a stack.
If you use an array directly, you can call other methods that aren't supposed to be part of the Stack interface. You can also use direct array indexing. The Stack class forces you to limit your operations to those defined by the class.
Defining a class allows you to change the implementation design. For instance, instead of using an array internally you could use a linked list.
Defining a class allows you to add additional methods like print(), without worrying about conflicts with methods that are defined on the built-in Array class.
While the in-built Array pop() and push() methods are most likely faster, the project you are working defines data as an Object and not an Array, so the methods have to be defined from scratch. Making data an Array seems more intuitive to me, but there may be a reason it is an Object instead.
Generally is better to use built in Javascript API, specially if the API is implemented in native code (as Arrays are). This Stack class is using a plain object to store the data and, due to this, has pretty much worse performance than even an hypothetical new Stack class wrapping an internal Array and delegating behaviour to the Array API (narrowing the Array interface to match a "Stack"). Objects and Arrays are implemented different in javascript engines, Arrays are way faster than objects. You can modify the current class to just wrap an internal array and expose only the methods you'd see in a Stack interface (and implementing your .peek() method as it doesn't exists in the original API).
Side note: The way the class is implemented is "valid" syntax, but not optimal. If you're going to use ES6+ (class keyword instead of constructor functions), better adopt full syntax, defining the instance methods outside the constructor, as it should be, and also defining the fields in instance level, not scoping it only inside the constructor.
Ok, because my initial question sounds unclear, so I decided to edit it. My question is how do you find out who defined a certain property, for example, the parseInt function, how do I know on which object it was definded like if parseInt was definded on the window object or the document object or whatever object it is? Thank you
I know the parseInt was definded the window object, I am just using it as an example in general, I am not specifically asking what object definded the parseInt property.
Also, please don't show me jQuery codes since I don't know jQuery that very good.
There is unfortunately no way to determine using code what the variable environment is of a given variable.
As for object properties, they should be obvious if they are myObj.property. If not obvious, it could be possible to use an exhaustive search to look for their existence in certain places, or certain known recursively.
Overall, it is not possible to know without looking at implementation documentation.
I know that to solve my question, we could use Object.prototype.hasOwnProperty(), but that would be very alot of typing because you have to type it out each time you need to know if a certain property is defined on a object. I have decided to write my own function to make this a little easier even though this is of no good practical use, I just wanted to satisfy my curiosity.
function findOwner(property, ownerObjectArray) {
var result = []; // Array to store the objects that the given property is defined on
for (var i = 1; i < arguments.length; i++)
{
var obj = arguments[i]; // the object currently being inspected
var properyList= Object.getOwnPropertyNames(arguments[i]); // a list of all "Owned" properties by this object
for (var j = 0; j < properyList.length; j++)
{
if (property === properyList[j]) result.push(obj.constructor);
}
}
return result.length > 0 ? result : "undefinded";
}
run this method
window.onload = run;
function run()
{
alert(findOwner("parseInt", Array.prototype, window, document)); // passing 3 objects we want to test against to this method. It printed : [object Window], the given property "parseInt" was found on the "Window" object
}
I want to cache large objects in JavaScript. These objects are retrieved by key, and it makes sense to cache them. But they won't fit in memory all at once, so I want them to be garbage collected if needed - the GC obviously knows better.
It is pretty trivial to make such a cache using WeakReference or WeakValueDictionary found in other languages, but in ES6 we have WeakMap instead, where keys are weak.
So, is it possible to make something like a WeakReference or make garbage-collected caches from WeakMap?
There are two scenarios where it's useful for a hash map to be weak (yours seems to fit the second):
One wishes to attach information to an object with a known identity; if the object ceases to exist, the attached information will become meaningless and should likewise cease to exist. JavaScript supports this scenario.
One wishes to merge references to semantically-identical objects, for the purposes of reducing storage requirements and expediting comparisons. Replacing many references to identical large subtrees, for example, with references to the same subtree can allow order-of-magnitude reductions in memory usage and execution time. Unfortunately JavaScript doesn't support this scenario.
In both cases, references in the table will be kept alive as long as they are useful, and will "naturally" become eligible for collection when they become useless. Unfortunately, rather than implementing separate classes for the two usages defined above, the designers of WeakReference made it so it can kinda-sorta be usable for either, though not terribly well.
In cases where the keys define equality to mean reference identity, WeakHashMap will satisfy the first usage pattern, but the second would be meaningless (code which held a reference to an object that was semantically identical to a stored key would hold a reference to the stored key, and wouldn't need the WeakHashMap to give it one). In cases where keys define some other form of equality, it generally doesn't make sense for a table query to return anything other than a reference to the stored object, but the only way to avoid having the stored reference keep the key alive is to use a WeakHashMap<TKey,WeakReference<TKey>> and have the client retrieve the weak reference, retrieve the key reference stored therein, and check whether it's still valid (it could get collected between the time the WeakHashMap returns the WeakReference and the time the WeakReference itself gets examined).
is it possible to make WeakReference from WeakMap or make garbage-collected cache from WeakMap ?
AFAIK the answer is "no" to both questions.
It's now possible thanks to FinalizationRegistry and WeakRef
Example:
const caches: Record<string, WeakRef<R>> = {}
const finalizer = new FinalizationRegistry((key: string) =>
{
console.log(`Finalizing cache: ${key}`)
delete caches[key]
})
function setCache(key: string, value: R)
{
const cache = getCache(key)
if (cache)
{
if (cache === value) return
finalizer.unregister(cache)
}
caches[key] = new WeakRef(value)
finalizer.register(value, key, value)
}
function getCache(key: string)
{
return caches[key]?.deref()
}
As the other answers mentioned, unfortunately there's no such thing as a weak map, like there is in Java / C#.
As a work around, I created this CacheMap that keeps a maximum number of objects around, and tracks their usage over a set period of time so that you:
Always remove the least accessed object, when necessary
Don't create a memory leak.
Here's the code.
"use strict";
/**
* This class keeps a maximum number of items, along with a count of items requested over the past X seconds.
*
* Unfortunately, in JavaScript, there's no way to create a weak map like in Java/C#.
* See https://stackoverflow.com/questions/25567578/garbage-collected-cache-via-javascript-weakmaps
*/
module.exports = class CacheMap {
constructor(maxItems, secondsToKeepACountFor) {
if (maxItems < 1) {
throw new Error("Max items must be a positive integer");
}
if (secondsToKeepACountFor < 1) {
throw new Error("Seconds to keep a count for must be a positive integer");
}
this.itemsToCounts = new WeakMap();
this.internalMap = new Map();
this.maxItems = maxItems;
this.secondsToKeepACountFor = secondsToKeepACountFor;
}
get(key) {
const value = this.internalMap.get(key);
if (value) {
this.itemsToCounts.get(value).push(CacheMap.getCurrentTimeInSeconds());
}
return value;
}
has(key) {
return this.internalMap.has(key);
}
static getCurrentTimeInSeconds() {
return Math.floor(Date.now() / 1000);
}
set(key, value) {
if (this.internalMap.has(key)) {
this.internalMap.set(key, value);
} else {
if (this.internalMap.size === this.maxItems) {
// Figure out who to kick out.
let keys = this.internalMap.keys();
let lowestKey;
let lowestNum = null;
let currentTime = CacheMap.getCurrentTimeInSeconds();
for (let key of keys) {
const value = this.internalMap.get(key);
let totalCounts = this.itemsToCounts.get(value);
let countsSince = totalCounts.filter(count => count > (currentTime - this.secondsToKeepACountFor));
this.itemsToCounts.set(value, totalCounts);
if (lowestNum === null || countsSince.length < lowestNum) {
lowestNum = countsSince.length;
lowestKey = key;
}
}
this.internalMap.delete(lowestKey);
}
this.internalMap.set(key, value);
}
this.itemsToCounts.set(value, []);
}
size() {
return this.internalMap.size;
}
};
And you call it like so:
// Keeps at most 10 client databases in memory and keeps track of their usage over a 10 min period.
let dbCache = new CacheMap(10, 600);
Scenario: I'm searching for a specific object in a deep object. I'm using a recursive function that goes through the children and asks them if I'm searching for them or if I'm searching for their children or grandchildren and so on. When found, the found obj will be returned, else false. Basically this:
obj.find = function (match_id) {
if (this.id == match_id) return this;
for (var i = 0; i < this.length; i++) {
var result = this[i].find(match_id);
if (result !== false) return result;
};
return false;
}
i'm wondering, is there something simpler than this?:
var result = this[i].find(match_id);
if (result) return result;
It annoys me to store the result in a variable (on each level!), i just want to check if it's not false and return the result. I also considered the following, but dislike it even more for obvious reasons.
if (this[i].find(match_id)) return this[i].find(match_id);
Btw I'm also wondering, is this approach even "recursive"? it isn't really calling itself that much...
Thank you very much.
[edit]
There is another possibility by using another function check_find (which just returns only true if found) in the if statement. In some really complicated cases (e.g. where you don't just find the object, but also alter it) this might be the best approach. Or am I wrong? D:
Although the solution you have is probably "best" as far as search algorithms go, and I wouldn't necessarily suggest changing it (or I would change it to use a map instead of an algorithm), the question is interesting to me, especially relating to the functional properties of the JavaScript language, and I would like to provide some thoughts.
Method 1
The following should work without having to explicitly declare variables within a function, although they are used as function arguments instead. It's also quite succinct, although a little terse.
var map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return this.id == match_id ? this : map(this, function(u) {
return find.call(u, match_id);
}).filter(function(u) { return u; })[0];
};
How it works:
We test to see if this.id == match_id, if so, return this.
We use map (via Array.prototype.map) to convert this to an array of "found items", which are found using the recursive call to the find method. (Supposedly, one of these recursive calls will return our answer. The ones which don't result in an answer will return undefined.)
We filter the "found items" array so that any undefined results in the array are removed.
We return the first item in the array, and call it quits.
If there is no first item in the array, undefined will be returned.
Method 2
Another attempt to solve this problem could look like this:
var concat = Function.prototype.call.bind(Array.prototype.concat),
map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return (function buildObjArray(o) {
return concat([ o ], map(o, buildObjArray));
})(this).filter(function(u) { return u.id == match_id })[0];
};
How it works:
buildObjArray builds a single, big, 1-dimensional array containing obj and all of obj's children.
Then we filter based on the criteria that an object in the array must have an id of match_id.
We return the first match.
Both Method 1 and Method 2, while interesting, have the performance disadvantage that they will continue to search even after they've found a matching id. They don't realize they have what they need until the end of the search, and this is not very efficient.
Method 3
It is certainly possible to improve the efficiency, and now I think this one really gets close to what you were interested in.
var forEach = Function.prototype.call.bind(Array.prototype.forEach);
obj.find = function(match_id) {
try {
(function find(obj) {
if(obj.id == match_id) throw this;
forEach(obj, find);
})(obj);
} catch(found) {
return found;
}
};
How it works:
We wrap the whole find function in a try/catch block so that once an item is found, we can throw and stop execution.
We create an internal find function (IIFE) inside the try which we reference to make recursive calls.
If this.id == match_id, we throw this, stopping our search algorithm.
If it doesn't match, we recursively call find on each child.
If it did match, the throw is caught by our catch block, and the found object is returned.
Since this algorithm is able to stop execution once the object is found, it would be close in performance to yours, although it still has the overhead of the try/catch block (which on old browsers can be expensive) and forEach is slower than a typical for loop. Still these are very small performance losses.
Method 4
Finally, although this method does not fit the confines of your request, it is much, much better performance if possible in your application, and something to think about. We rely on a map of ids which maps to objects. It would look something like this:
// Declare a map object.
var map = { };
// ...
// Whenever you add a child to an object...
obj[0] = new MyObject();
// .. also store it in the map.
map[obj[0].id] = obj[0];
// ...
// Whenever you want to find the object with a specific id, refer to the map:
console.log(map[match_id]); // <- This is the "found" object.
This way, no find method is needed at all!
The performance gains in your application by using this method will be HUGE. Please seriously consider it, if at all possible.
However, be careful to remove the object from the map whenever you will no longer be referencing that object.
delete map[obj.id];
This is necessary to prevent memory leaks.
No there is no other clear way, storing the result in a variable isn't that much trouble, actually this is what variables are used for.
Yes, that approach is recursive:
you have the base case if (this.id==match_id) return this
you have the recursive step which call itself obj.find(match_id) { ... var result = this[i].find(match_id); }
I don't see any reason, why storing the variable would be bad. It's not a copy, but a reference, so it's efficient. Plus the temporary variable is the only way, that I can see right now (I may be wrong, though).
With that in mind, I don't think, that a method check_find would make very much sense (it's most probably basically the same implementation), so if you really need this check_find method, I'd implement it as
return this.find(match_id) !== false;
Whether the method is recursive is hard to say.
Basically, I'd say yes, as the implementations of 'find' are all the same for every object, so it's pretty much the same as
function find(obj, match_id) {
if (obj.id == match_id) return obj;
for (var i = 0; i < obj.length; ++i) {
var result = find(obj[i], match_id);
if (result !== false) return result;
}
}
which is definitely recursive (the function calls itself).
However, if you'd do
onesingleobjectinmydeepobject.find = function(x) { return this; }
I'm not quite sure, if you still would call this recursive.
Because jQuery is a widely used and mature collaborative effort, I can't help but to look at its source for guidance in writing better Javascript. I use the jQuery library all the time along with my PHP applications, but when I look under the hood of this rather sophisticated library I realize just how much I still don't understand about Javascript. Lo, I have a few questions for the SO community. First of all, consider the following code...
$('#element').attr('alt', 'Ivan is SUPER hungry! lolz');
vs
$('#element').attr({'alt': 'Ivan is an ugly monster! omfgz'});
Now, is this to say that the attr() method was designed to accept EITHER an attribute name, an attribute name and a value, or a pair-value map? Can someone give me a short explanation of what a map actually is and the important ways that it differs from an array in Javascript?
Moving on, the whole library is wrapped in this business...
(function(window, undefined) { /* jQuery */ })(window);
I get that the wrapped parentheses cause a behavior similar to body onLoad="function();", but what is this practice called and is it any different than using the onLoad event handler? Also, I can't make heads or tails of the (window) bit there at the end. What exactly is happening with the window object here?
Am I wrong in the assessment that objects are no different than functions in Javascript? Please correct me if I'm wrong on this but $() is the all encompassing jQuery object, but it looks just like a method. Here's another quick question with a code example...
$('#element').attr('alt', 'Adopt a Phantom Cougar from Your Local ASPCA');
... Should look something like this on the inside (maybe I'm wrong about this)...
function $(var element = null) {
if (element != null) {
function attr(var attribute = null, var value = null) {
/* stuff that does things */
}
}
}
Is this the standing procedure for defining objects and their child methods and properties in Javascript? Comparing Javascript to PHP, do you use a period . the same way you would use -> to retrieve a method from an object?
I apologize for this being a bit lengthy, but answers to these questions will reveal a great deal to me about jQuery and Javascript in general. Thanks!
1. Method overloading
$('#element').attr('alt', 'Ivan is SUPER hungry! lolz');
vs
$('#element').attr({'alt': 'Ivan is an ugly monster! omfgz'});
var attr = function (key, value) {
// is first argument an object / map ?
if (typeof key === "object") {
// for each key value pair
for (var k in key) {
// recursively call it.
attr(k, key[k]);
}
} else {
// do magic with key and value
}
}
2. Closures
(function(window, undefined) { /* jQuery */ })(window);
Is not used as an onload handler. It's simply creating new scope inside a function.
This means that var foo is a local variable rather then a global one. It's also creating a real undefined variable to use since Parameters that are not specified passes in undefined
This gaurds againts window.undefined = true which is valid / allowed.
the (window) bit there at the end. What exactly is happening with the window object here?
It's micro optimising window access by making it local. Local variable access is about 0.01% faster then global variable access
Am I wrong in the assessment that objects are no different than functions in Javascript?
Yes and no. All functions are objects. $() just returns a new jQuery object because internally it calls return new jQuery.fn.init();
3. Your snippet
function $(var element = null) {
Javascript does not support default parameter values or optional parameters. Standard practice to emulate this is as follows
function f(o) {
o != null || (o = "default");
}
Comparing Javascript to PHP, do you use a period . the same way you would use -> to retrieve a method from an object?
You can access properties on an object using foo.property or foo["property"] a property can be any type including functions / methods.
4. Miscellanous Questions hidden in your question
Can someone give me a short explanation of what a map actually is and the important ways that it differs from an array in Javascript?
An array is created using var a = [] it simply contains a list of key value pairs where all the keys are positive numbers. It also has all the Array methods. Arrays are also objects.
A map is just an object. An object is simply a bag of key value pairs. You assign some data under a key on the object. This data can be of any type.
For attr, if you give an object instead of a key value pair it will loop on each property.
Look for attr: in jQuery's code, then you'll see it use access. Then look for access: and you will see there is a check on the type of key if it is an object, start a loop.
The wrapping in a function, is to prevent all the code inside to be accessed from outside, and cause unwanted problems. The only parameters that are passed are window that allow to set globals and access the DOM. The undefined I guess it is to make the check on this special value quicker.
I read sometimes jQuery but I didn't start with it, may be you should get some good books to make you an idea first of what some advanced features Javascript has, and then apply your knowledge to the specifics of jQuery.
1 - Yes attr can accept a attribute name for getting a value, a name and a value for setting one value or a map of attribute names and values for settings more than one attribute
2 - A map is basically a JavaScript object e.g:
var map = {
'key1' : 'value1',
'key2' : 'value2'
};
3 - (function(window, undefined) { /* jQuery */ })(window); is something called an anonymous function as it doesn't have a name. In this case it also executes straight away.
A simple example would be:
function test(){
...
}
test();
//As an anonymous function it would be:
(function(){
...
}();
//And it you wanted to pass variables:
function test(abc){
...
}
test(abc);
//As an anonymous function it would be:
(function(abc){
...
}(abc);
this would make it different to the load event, as it is a function not an event.
4 - window is passed as a variable, as it is used internally within jQuery
5 - Objects and functions the same, as everything in JavaScript is an object. jQuery does something like this:
var obj = {
"init" : function(){
}
}
6 - Yes you can use . to retrieve a value on an object but you can also use [] e.g:
var map = {
"test" : 1
}
map.test //1
map["test"] //1
I hope this answers your many questions, let me know if I've missed anything out.
jQuery 1.6.1
The test is typeof key === "object"
if that is true, then you passed a { .... }
jQuery.fn.extend({
attr: function( name, value ) {
return jQuery.access( this, name, value, true, jQuery.attr );
},
// Mutifunctional method to get and set values to a collection
// The value/s can be optionally by executed if its a function
access: function( elems, key, value, exec, fn, pass ) {
var length = elems.length;
// Setting many attributes
if ( typeof key === "object" ) {
for ( var k in key ) {
jQuery.access( elems, k, key[k], exec, fn, value );
}
return elems;
}
// Setting one attribute
if ( value !== undefined ) {
// Optionally, function values get executed if exec is true
exec = !pass && exec && jQuery.isFunction(value);
for ( var i = 0; i < length; i++ ) {
fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
}
return elems;
}
// Getting an attribute
return length ? fn( elems[0], key ) : undefined;
},