Make JSON dump of JS-object window.navigator [duplicate] - javascript

I have an object (parse tree) that contains child nodes which are references to other nodes.
I'd like to serialize this object, using JSON.stringify(), but I get
TypeError: cyclic object value
because of the constructs I mentioned.
How could I work around this? It does not matter to me whether these references to other nodes are represented or not in the serialized object.
On the other hand, removing these properties from the object when they are being created seems tedious and I wouldn't want to make changes to the parser (narcissus).

Use the second parameter of stringify, the replacer function, to exclude already serialized objects:
var seen = [];
JSON.stringify(obj, function(key, val) {
if (val != null && typeof val == "object") {
if (seen.indexOf(val) >= 0) {
return;
}
seen.push(val);
}
return val;
});
http://jsfiddle.net/mH6cJ/38/
As correctly pointed out in other comments, this code removes every "seen" object, not only "recursive" ones.
For example, for:
a = {x:1};
obj = [a, a];
the result will be incorrect. If your structure is like this, you might want to use Crockford's decycle or this (simpler) function which just replaces recursive references with nulls:
function decycle(obj, stack = []) {
if (!obj || typeof obj !== 'object')
return obj;
if (stack.includes(obj))
return null;
let s = stack.concat([obj]);
return Array.isArray(obj)
? obj.map(x => decycle(x, s))
: Object.fromEntries(
Object.entries(obj)
.map(([k, v]) => [k, decycle(v, s)]));
}
//
let a = {b: [1, 2, 3]}
a.b.push(a);
console.log(JSON.stringify(decycle(a)))

This is kind of an alternate-answer, but since what a lot of people will come here for is debugging their circular objects and there's not really a great way to do that without pulling in a bunch of code, here goes.
One feature that's not as well-known as JSON.stringify() is console.table(). Simply call console.table(whatever);, and it will log the variable in the console in tabular format, making it rather quite easy and convenient to peruse the variable's contents.

Here is an example of a data structure with cyclic references:
function makeToolshed(){
var nut = {name: 'nut'}, bolt = {name: 'bolt'};
nut.needs = bolt; bolt.needs = nut;
return { nut: nut, bolt: bolt };
}
When you wish to KEEP the cyclic references (restore them when you deserialize, instead of "nuking" them), you have 2 choices, which I'll compare here. First is Douglas Crockford's cycle.js, second is my siberia package. Both work by first "decycling" the object, i.e., constructing another object (without any cyclic references) "containing the same information."
Mr. Crockford goes first:
JSON.decycle(makeToolshed())
As you see, the nested structure of JSON is retained, but there is a new thing, which is objects with the special $ref property. Let's see how that works.
root = makeToolshed();
[root.bolt === root.nut.needs, root.nut.needs.needs === root.nut]; // retutrns [true,true]
The dollar sign stands for the root. .bolt having $ref tells us that .bolt is an "already seen" object, and the value of that special property (here, the string $["nut"]["needs"]) tells us where, see first === above. Likewise for second $ref and the second === above.
Let's use a suitable deep equality test (namely Anders Kaseorg's deepGraphEqual function from accepted answer to this question) to see if cloning works.
root = makeToolshed();
clone = JSON.retrocycle(JSON.decycle(root));
deepGraphEqual(root, clone) // true
serialized = JSON.stringify(JSON.decycle(root));
clone2 = JSON.retrocycle(JSON.parse(serialized));
deepGraphEqual(root, clone2); // true
Now, siberia:
JSON.Siberia.forestify(makeToolshed())
Siberia does not try to mimic "classic" JSON, no nested structure. The object graph is described in a "flat" manner.
Each node of the object graph is turned into a flat tree (plain key value pair list with integer-only values), which is an entry in .forest. At index zero, we find the root object, at higher indices, we find the other nodes of the object graph, and negative values (of some key of some tree of the forest) point to the atoms array, (which is typed via the types array, but we'll skip the typing details here). All terminal nodes are in the atoms table, all non-terminal nodes are in the forest table, and you can see right away how many nodes the object graph has, namely forest.length. Let's test if it works:
root = makeToolshed();
clone = JSON.Siberia.unforestify(JSON.Siberia.forestify(root));
deepGraphEqual(root, clone); // true
serialized = JSON.Siberia.stringify(JSON.Siberia.forestify(root));
clone2 = JSON.Siberia.unforestify(JSON.Siberia.unstringify(serialized));
deepGraphEqual(root, clone2); // true
comparison
will add section later.
note
I'm currently refactoring the package. Central ideas and algorithms are staying the same, but the new version will be easier to use, the top level API will be different. I will very soon archive siberia and present the refactored version, which I'll call objectgraph. Stay tuned, it will happen this month (August 2020)
ah, and ultra short version for the comparison. For a "pointer", I need as much space as an integer takes, since my "pointers to already seen nodes" (as a matter of fact, to all nodes, already seen or not) are just integers. In Mr. Crockford's version, amount needed to store a "pointer" is bounded only by the size of the object graph. That makes the worst case complexity of Mr. Crockford's version extremely horrible. Mr. Crockford gave us "another Bubblesort". I'm not kidding you. It's that bad. If you don't believe it, there are tests, you can find them starting from the readme of the package (will transform them to be benchmark.js compliant also this month, Aug 2020)

much saver and it shows where an cycle object was.
<script>
var jsonify=function(o){
var seen=[];
var jso=JSON.stringify(o, function(k,v){
if (typeof v =='object') {
if ( !seen.indexOf(v) ) { return '__cycle__'; }
seen.push(v);
} return v;
});
return jso;
};
var obj={
g:{
d:[2,5],
j:2
},
e:10
};
obj.someloopshere = [
obj.g,
obj,
{ a: [ obj.e, obj ] }
];
console.log('jsonify=',jsonify(obj));
</script>
produces
jsonify = {"g":{"d":[2,5],"j":2},"e":10,"someloopshere":[{"d":[2,5],"j":2},"__cycle__",{"a":[10,"__cycle__"]}]}

I've created an GitHub Gist which is able to detect cyclic structures and also de- and encodes them: https://gist.github.com/Hoff97/9842228
To transform just use JSONE.stringify/JSONE.parse.
It also de- and encodes functions. If you want to disable this just remove lines 32-48 and 61-85.
var strg = JSONE.stringify(cyclicObject);
var cycObject = JSONE.parse(strg);
You can find an example fiddle here:
http://jsfiddle.net/hoff97/7UYd4/

I create too a github project that can serialize cyclic object and restore the class if you save it in the serializename attribute like a String
var d={}
var a = {b:25,c:6,enfant:d};
d.papa=a;
var b = serializeObjet(a);
assert.equal( b, "{0:{b:25,c:6,enfant:'tab[1]'},1:{papa:'tab[0]'}}" );
var retCaseDep = parseChaine(b)
assert.equal( retCaseDep.b, 25 );
assert.equal( retCaseDep.enfant.papa, retCaseDep );
https://github.com/bormat/serializeStringifyParseCyclicObject
Edit:
I have transform my script for NPM https://github.com/bormat/borto_circular_serialize and I have change function names from french to english.

the nodejs module serialijse provides a nice way to deal with any type of JSON objects containing cycles or javascript class instances.
const { serialize, deserialize } = require("serialijse");
var Mary = { name: "Mary", friends: [] };
var Bob = { name: "Bob", friends: [] };
Mary.friends.push(Bob);
Bob.friends.push(Mary);
var group = [ Mary, Bob];
console.log(group);
// testing serialization using JSON.stringify/JSON.parse
try {
var jstr = JSON.stringify(group);
var jo = JSON.parse(jstr);
console.log(jo);
} catch (err) {
console.log(" JSON has failed to manage object with cyclic deps");
console.log(" and has generated the following error message", err.message);
}
// now testing serialization using serialijse serialize/deserialize
var str = serialize(group);
var so = deserialize(str);
console.log(" However Serialijse knows to manage object with cyclic deps !");
console.log(so);
assert(so[0].friends[0] == so[1]); // Mary's friend is Bob
this serializer supports
cycle in the object definition
reconstruction of class's instance
support for Typed Array, Map, and Set
ability to filter properties to skip during the serialization process.
binary encoding of Typed Array (Float32Array etc ... ) for performance.

function stringifyObject ( obj ) {
if ( _.isArray( obj ) || !_.isObject( obj ) ) {
return obj.toString()
}
var seen = [];
return JSON.stringify(
obj,
function( key, val ) {
if (val != null && typeof val == "object") {
if ( seen.indexOf( val ) >= 0 )
return
seen.push( val )
}
return val
}
);
}
A precondition was missing, otherwise the integer values in array objects are truncated, i.e. [[ 08.11.2014 12:30:13, 1095 ]] 1095 gets reduced to 095.

Related

Where can I see the source code for JavaScript methods, such as hasOwnProperty, in Node.js?

I have been studying JavaScript algorithms and Big O for interviews. I was told that knowing the runtimes of built-in methods, such as Object.prototype.hasOwnProperty and Array.prototype.map, is important.
What is a simple way to view the source code for these functions in node.js? I have a local copy of node.js, and I tried to search for these methods in my text editor, but it's not as straightforward as I thought.
Object.prototype.hasOwnProperty()
From a Javascript interview point of view, I would think you just need to fully understand what obj.hasOwnProperty() does at the Javascript level, not how it's implemented inside of V8.
To do that, you should fully understand this little snippet:
function MyConstructor() {
this.methodB = function() {}
}
MyConstructor.prototype = {
methodA: function() {}
};
var o = new MyConstructor();
log(o.hasOwnProperty("methodA")); // false
log(o.hasOwnProperty("methodB")); // true
o.methodA = function() {}; // assign "own" property, overrides prototype
log(o.hasOwnProperty("methodA")); // true
This is because .hasOwnProperty() looks only on the object itself and not on the prototype chain. So properties which are only on the prototype chain or do not exist at all will return false and properties which are directly on the object will return true.
Array.prototype.map()
A polyfill in Javascript for Array.prototype.map() is here on MDN which will show you exactly how it works. You can, of course, do the same type of search I did above in the Github repository to find the .map() implementation too if you want.
Array.prototype.map() is pretty simple really. Iterate over an array, calling a function for each item in the array. Each return value of that function will be used to construct a new array that will be returned from the call to .map(). So, conceptually, it's used to "map" one array to another by calling some transform function on each element of the original array.
In the simplest incarnation, you add 1 to each element of an array:
var origArray = [1,2,3];
var newArray = origArray.map(function(item, index, array) {
return item + 1;
});
console.log(newArray); // [2,3,4]
Actual V8 source code:
If you really want to see how it is implemented inside of V8, here are code snippets and links to the relevant actual code files. As you can see, most of it is in C++ and to understand it, you have to understand how objects are structured in memory and what C++ methods they have internally in V8. This is very V8-specific, not general Javascript knowledge.
I've included links to the relevant source files too so if you want to see other context in those files, you can click on the links to see that.
In v8.h:
V8_DEPRECATED("Use maybe version", bool HasOwnProperty(Local<String> key));
V8_WARN_UNUSED_RESULT Maybe<bool> HasOwnProperty(Local<Context> context, Local<Name> key);
In api.cc:
Maybe<bool> v8::Object::HasOwnProperty(Local<Context> context,
Local<Name> key) {
PREPARE_FOR_EXECUTION_PRIMITIVE(context, "v8::Object::HasOwnProperty()",
bool);
auto self = Utils::OpenHandle(this);
auto key_val = Utils::OpenHandle(*key);
auto result = i::JSReceiver::HasOwnProperty(self, key_val);
has_pending_exception = result.IsNothing();
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return result;
}
bool v8::Object::HasOwnProperty(Local<String> key) {
auto context = ContextFromHeapObject(Utils::OpenHandle(this));
return HasOwnProperty(context, key).FromMaybe(false);
}
In v8natives.js:
// ES6 7.3.11
function ObjectHasOwnProperty(value) {
var name = TO_NAME(value);
var object = TO_OBJECT(this);
return %HasOwnProperty(object, name);
}
In objects-inl.h:
Maybe<bool> JSReceiver::HasOwnProperty(Handle<JSReceiver> object,
Handle<Name> name) {
if (object->IsJSObject()) { // Shortcut
LookupIterator it = LookupIterator::PropertyOrElement(
object->GetIsolate(), object, name, LookupIterator::HIDDEN);
return HasProperty(&it);
}
Maybe<PropertyAttributes> attributes =
JSReceiver::GetOwnPropertyAttributes(object, name);
MAYBE_RETURN(attributes, Nothing<bool>());
return Just(attributes.FromJust() != ABSENT);
}
In runtime-object.cc:
static Object* HasOwnPropertyImplementation(Isolate* isolate,
Handle<JSObject> object,
Handle<Name> key) {
Maybe<bool> maybe = JSReceiver::HasOwnProperty(object, key);
if (!maybe.IsJust()) return isolate->heap()->exception();
if (maybe.FromJust()) return isolate->heap()->true_value();
// Handle hidden prototypes. If there's a hidden prototype above this thing
// then we have to check it for properties, because they are supposed to
// look like they are on this object.
if (object->map()->has_hidden_prototype()) {
PrototypeIterator iter(isolate, object);
DCHECK(!iter.IsAtEnd());
// TODO(verwaest): The recursion is not necessary for keys that are array
// indices. Removing this.
// Casting to JSObject is fine because JSProxies are never used as
// hidden prototypes.
return HasOwnPropertyImplementation(
isolate, PrototypeIterator::GetCurrent<JSObject>(iter), key);
}
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
return isolate->heap()->false_value();
}
RUNTIME_FUNCTION(Runtime_HasOwnProperty) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0)
CONVERT_ARG_HANDLE_CHECKED(Name, key, 1);
uint32_t index;
const bool key_is_array_index = key->AsArrayIndex(&index);
// Only JS objects can have properties.
if (object->IsJSObject()) {
Handle<JSObject> js_obj = Handle<JSObject>::cast(object);
// Fast case: either the key is a real named property or it is not
// an array index and there are no interceptors or hidden
// prototypes.
// TODO(jkummerow): Make JSReceiver::HasOwnProperty fast enough to
// handle all cases directly (without this custom fast path).
Maybe<bool> maybe = Nothing<bool>();
if (key_is_array_index) {
LookupIterator it(js_obj->GetIsolate(), js_obj, index,
LookupIterator::HIDDEN);
maybe = JSReceiver::HasProperty(&it);
} else {
maybe = JSObject::HasRealNamedProperty(js_obj, key);
}
if (!maybe.IsJust()) return isolate->heap()->exception();
DCHECK(!isolate->has_pending_exception());
if (maybe.FromJust()) {
return isolate->heap()->true_value();
}
Map* map = js_obj->map();
if (!key_is_array_index && !map->has_named_interceptor() &&
!map->has_hidden_prototype()) {
return isolate->heap()->false_value();
}
// Slow case.
return HasOwnPropertyImplementation(isolate, Handle<JSObject>(js_obj),
Handle<Name>(key));
} else if (object->IsString() && key_is_array_index) {
// Well, there is one exception: Handle [] on strings.
Handle<String> string = Handle<String>::cast(object);
if (index < static_cast<uint32_t>(string->length())) {
return isolate->heap()->true_value();
}
} else if (object->IsJSProxy()) {
Maybe<bool> result =
JSReceiver::HasOwnProperty(Handle<JSProxy>::cast(object), key);
if (!result.IsJust()) return isolate->heap()->exception();
return isolate->heap()->ToBoolean(result.FromJust());
}
return isolate->heap()->false_value();
}
This is the node.js Github repository. If you know what to search for and have enough patience to wade through all the search hits, you can generally find anything you need. The unfortunate thing about searching on Github is I have not found any way to remove all the test sub-directories from the search so you end up with 95% of the search hits in the test code, not in the actual implementation code. But, with enough persistence, you can eventually find what you need.

Most elegant way to save only primitive properties of a Javascript object

I have an object with lots of own and inherited methods. I need to make a simple snapshot of it with only primitive properties.
What's the most elegant way how to do it?
Javascript snippet
function copy(source) {
var target;
if (source) {
target = {};
for (var key in source) {
var prop = source[key],
type = typeof prop;
if (type === "string" || type === "number" || type === "boolean") {
target[key] = prop;
}
if (type == "object") {
target[key] = copy(prop);
}
}
}
return target;
}
With Underscore.js
function copy(source) {
var target;
if (source) {
target = {};
_.filter( _.keys(source), function( key ) {
var prop = source[key];
if (!_.isFunction(prop) && !_.isObject(prop)) {
target[key] = prop;
}
if (_.isObject(prop)) {
target[key] = copy(prop);
}
});
}
return target;
}
The problem here is that you are dealing with a mix of types that may or may not be what you want thanks to the loose typing that JavaScript offers. Specifically without walking any given array it is impossible to tell whether it only contains primitives and you might therefore want to keep it or whether it contains objects or functions that you want to filter out.
I realise library recommendations are the oldest hat, but I'm also going to strongly recommend that you use Underscore.js ( or a derivative such as Lodash ) which gives a whole lot of super-convenient functions that I use the whole time. In a very real sense these feel like the missing part of the core JavaScript language - in fact in some cases they are wrapping native functionality, but allowing you to avoid platform inconsistencies.
If you have Underscore included in your library then you can find the non-object property names of any given object like this:
var myObject = {
a: "hello",
b: "goodbye",
c: function() { console.log(this.b);}
d: { hello: "banana" }
};
_.filter( _.keys(myObject), function( key ) {
return ! _.isObject(myObject[key]);
}) // => ["a", "b"]
This could then be tidied into a function to clone the object, but of course it's unnecessary because Underscore offers us _.pick, which returns a copy of the object with only the whitelisted properties:
_.pick( myObject, _.filter( _.keys(myObject), function( key ) { return ! _.isObject(myObject[key]);}))
By flipping the !_.isObject you can retrieve all the properties which are objects, and perform a similar mapping on those if necessary. By wrapping both in a function you can call it recursively on the child objects you want to clone.
This answers your question, but be aware that this might not be the best way to perform undo/redo actions, which you mention in your comments is the use you plan to put this to. It may be easier to record actions and states onto a history stack recording only the actual changes any action performs, where they can be retrieved and reverted safely without needing to keep snapshots of your data.

Serializing object that contains cyclic object value

I have an object (parse tree) that contains child nodes which are references to other nodes.
I'd like to serialize this object, using JSON.stringify(), but I get
TypeError: cyclic object value
because of the constructs I mentioned.
How could I work around this? It does not matter to me whether these references to other nodes are represented or not in the serialized object.
On the other hand, removing these properties from the object when they are being created seems tedious and I wouldn't want to make changes to the parser (narcissus).
Use the second parameter of stringify, the replacer function, to exclude already serialized objects:
var seen = [];
JSON.stringify(obj, function(key, val) {
if (val != null && typeof val == "object") {
if (seen.indexOf(val) >= 0) {
return;
}
seen.push(val);
}
return val;
});
http://jsfiddle.net/mH6cJ/38/
As correctly pointed out in other comments, this code removes every "seen" object, not only "recursive" ones.
For example, for:
a = {x:1};
obj = [a, a];
the result will be incorrect. If your structure is like this, you might want to use Crockford's decycle or this (simpler) function which just replaces recursive references with nulls:
function decycle(obj, stack = []) {
if (!obj || typeof obj !== 'object')
return obj;
if (stack.includes(obj))
return null;
let s = stack.concat([obj]);
return Array.isArray(obj)
? obj.map(x => decycle(x, s))
: Object.fromEntries(
Object.entries(obj)
.map(([k, v]) => [k, decycle(v, s)]));
}
//
let a = {b: [1, 2, 3]}
a.b.push(a);
console.log(JSON.stringify(decycle(a)))
This is kind of an alternate-answer, but since what a lot of people will come here for is debugging their circular objects and there's not really a great way to do that without pulling in a bunch of code, here goes.
One feature that's not as well-known as JSON.stringify() is console.table(). Simply call console.table(whatever);, and it will log the variable in the console in tabular format, making it rather quite easy and convenient to peruse the variable's contents.
Here is an example of a data structure with cyclic references:
function makeToolshed(){
var nut = {name: 'nut'}, bolt = {name: 'bolt'};
nut.needs = bolt; bolt.needs = nut;
return { nut: nut, bolt: bolt };
}
When you wish to KEEP the cyclic references (restore them when you deserialize, instead of "nuking" them), you have 2 choices, which I'll compare here. First is Douglas Crockford's cycle.js, second is my siberia package. Both work by first "decycling" the object, i.e., constructing another object (without any cyclic references) "containing the same information."
Mr. Crockford goes first:
JSON.decycle(makeToolshed())
As you see, the nested structure of JSON is retained, but there is a new thing, which is objects with the special $ref property. Let's see how that works.
root = makeToolshed();
[root.bolt === root.nut.needs, root.nut.needs.needs === root.nut]; // retutrns [true,true]
The dollar sign stands for the root. .bolt having $ref tells us that .bolt is an "already seen" object, and the value of that special property (here, the string $["nut"]["needs"]) tells us where, see first === above. Likewise for second $ref and the second === above.
Let's use a suitable deep equality test (namely Anders Kaseorg's deepGraphEqual function from accepted answer to this question) to see if cloning works.
root = makeToolshed();
clone = JSON.retrocycle(JSON.decycle(root));
deepGraphEqual(root, clone) // true
serialized = JSON.stringify(JSON.decycle(root));
clone2 = JSON.retrocycle(JSON.parse(serialized));
deepGraphEqual(root, clone2); // true
Now, siberia:
JSON.Siberia.forestify(makeToolshed())
Siberia does not try to mimic "classic" JSON, no nested structure. The object graph is described in a "flat" manner.
Each node of the object graph is turned into a flat tree (plain key value pair list with integer-only values), which is an entry in .forest. At index zero, we find the root object, at higher indices, we find the other nodes of the object graph, and negative values (of some key of some tree of the forest) point to the atoms array, (which is typed via the types array, but we'll skip the typing details here). All terminal nodes are in the atoms table, all non-terminal nodes are in the forest table, and you can see right away how many nodes the object graph has, namely forest.length. Let's test if it works:
root = makeToolshed();
clone = JSON.Siberia.unforestify(JSON.Siberia.forestify(root));
deepGraphEqual(root, clone); // true
serialized = JSON.Siberia.stringify(JSON.Siberia.forestify(root));
clone2 = JSON.Siberia.unforestify(JSON.Siberia.unstringify(serialized));
deepGraphEqual(root, clone2); // true
comparison
will add section later.
note
I'm currently refactoring the package. Central ideas and algorithms are staying the same, but the new version will be easier to use, the top level API will be different. I will very soon archive siberia and present the refactored version, which I'll call objectgraph. Stay tuned, it will happen this month (August 2020)
ah, and ultra short version for the comparison. For a "pointer", I need as much space as an integer takes, since my "pointers to already seen nodes" (as a matter of fact, to all nodes, already seen or not) are just integers. In Mr. Crockford's version, amount needed to store a "pointer" is bounded only by the size of the object graph. That makes the worst case complexity of Mr. Crockford's version extremely horrible. Mr. Crockford gave us "another Bubblesort". I'm not kidding you. It's that bad. If you don't believe it, there are tests, you can find them starting from the readme of the package (will transform them to be benchmark.js compliant also this month, Aug 2020)
much saver and it shows where an cycle object was.
<script>
var jsonify=function(o){
var seen=[];
var jso=JSON.stringify(o, function(k,v){
if (typeof v =='object') {
if ( !seen.indexOf(v) ) { return '__cycle__'; }
seen.push(v);
} return v;
});
return jso;
};
var obj={
g:{
d:[2,5],
j:2
},
e:10
};
obj.someloopshere = [
obj.g,
obj,
{ a: [ obj.e, obj ] }
];
console.log('jsonify=',jsonify(obj));
</script>
produces
jsonify = {"g":{"d":[2,5],"j":2},"e":10,"someloopshere":[{"d":[2,5],"j":2},"__cycle__",{"a":[10,"__cycle__"]}]}
I've created an GitHub Gist which is able to detect cyclic structures and also de- and encodes them: https://gist.github.com/Hoff97/9842228
To transform just use JSONE.stringify/JSONE.parse.
It also de- and encodes functions. If you want to disable this just remove lines 32-48 and 61-85.
var strg = JSONE.stringify(cyclicObject);
var cycObject = JSONE.parse(strg);
You can find an example fiddle here:
http://jsfiddle.net/hoff97/7UYd4/
I create too a github project that can serialize cyclic object and restore the class if you save it in the serializename attribute like a String
var d={}
var a = {b:25,c:6,enfant:d};
d.papa=a;
var b = serializeObjet(a);
assert.equal( b, "{0:{b:25,c:6,enfant:'tab[1]'},1:{papa:'tab[0]'}}" );
var retCaseDep = parseChaine(b)
assert.equal( retCaseDep.b, 25 );
assert.equal( retCaseDep.enfant.papa, retCaseDep );
https://github.com/bormat/serializeStringifyParseCyclicObject
Edit:
I have transform my script for NPM https://github.com/bormat/borto_circular_serialize and I have change function names from french to english.
the nodejs module serialijse provides a nice way to deal with any type of JSON objects containing cycles or javascript class instances.
const { serialize, deserialize } = require("serialijse");
var Mary = { name: "Mary", friends: [] };
var Bob = { name: "Bob", friends: [] };
Mary.friends.push(Bob);
Bob.friends.push(Mary);
var group = [ Mary, Bob];
console.log(group);
// testing serialization using JSON.stringify/JSON.parse
try {
var jstr = JSON.stringify(group);
var jo = JSON.parse(jstr);
console.log(jo);
} catch (err) {
console.log(" JSON has failed to manage object with cyclic deps");
console.log(" and has generated the following error message", err.message);
}
// now testing serialization using serialijse serialize/deserialize
var str = serialize(group);
var so = deserialize(str);
console.log(" However Serialijse knows to manage object with cyclic deps !");
console.log(so);
assert(so[0].friends[0] == so[1]); // Mary's friend is Bob
this serializer supports
cycle in the object definition
reconstruction of class's instance
support for Typed Array, Map, and Set
ability to filter properties to skip during the serialization process.
binary encoding of Typed Array (Float32Array etc ... ) for performance.
function stringifyObject ( obj ) {
if ( _.isArray( obj ) || !_.isObject( obj ) ) {
return obj.toString()
}
var seen = [];
return JSON.stringify(
obj,
function( key, val ) {
if (val != null && typeof val == "object") {
if ( seen.indexOf( val ) >= 0 )
return
seen.push( val )
}
return val
}
);
}
A precondition was missing, otherwise the integer values in array objects are truncated, i.e. [[ 08.11.2014 12:30:13, 1095 ]] 1095 gets reduced to 095.

Storing Tree Structures in JSON

I'm trying to write a system where the client (browser-based) requests a tree to be returned from the server (app engine, but that's irrelevant). The problem is in converting the tree to JSON: because each object refers to the objects 'below' it, when stringified I end up with an extremely long string which, on parsing, creates new objects for each child instead of references to other nodes.
My current solution would be to write an 'equalTo' and 'toString' function (converting object references to strings) stringify the resulting array and then recreate it on the client side by resolving strings to objects. That solution is making my terrible-algorithm-senses tingle, though, there must be a better way to return such structures through JSON!
EDIT: it just occurred to me that object references could also be converted to array indexes. It's a better solution, but still has that niggling bad-code feel to it.
EDIT2: Right, so I suppose some pseudo-code is in order, then.
var node = {
children : null;
};
var root = Object.create(node);
var level1a = Object.create(node);
var level1b = Object.create(node);
var level2a = Object.create(node);
var level2b = Object.create(node);
root.children = [level1a, level1b];
level1a.children = [level2a, level2b];
So you end up with a tree that looks like this:
stackoverflow won't let me post images
If you have a way to address nodes, you can use JSON revivers and replacers to convert between addresses and references.
For example, if you have two functions like
function addressForNode(node) { ... }
function nodeForAddress(address) { ... }
you could use revivers and replacers that call those when parsing or stringifying
var jsonString = JSON.stringify(tree, function (key, value) {
if (typeof value === 'object') {
var address = addressForNode(value);
if (address !== null) { // value really is a node
return { address: address }
}
}
return value;
});
// And to parse...
var tree = JSON.parse(jsonString, function (key, value) {
if (typeof value === 'object' && typeof value.address === 'string') {
return nodeForAddress(value.address);
}
return value;
});
If you can help it, don't restrict yourself to JSON. Instead consider generating JavaScript code included via linked script, using JSONP techniques if necessary.

Is there hash code function accepting any object type?

Basically, I'm trying to create an object of unique objects, a set. I had the brilliant idea of just using a JavaScript object with objects for the property names. Such as,
set[obj] = true;
This works, up to a point. It works great with string and numbers, but with other objects, they all seem to "hash" to the same value and access the same property. Is there some kind of way I can generate a unique hash value for an object? How do strings and numbers do it, can I override the same behavior?
If you want a hashCode() function like Java's in JavaScript, that is yours:
function hashCode(string){
var hash = 0;
for (var i = 0; i < string.length; i++) {
var code = string.charCodeAt(i);
hash = ((hash<<5)-hash)+code;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
That is the way of implementation in Java (bitwise operator).
Please note that hashCode could be positive and negative, and that's normal, see HashCode giving negative values. So, you could consider to use Math.abs() along with this function.
JavaScript objects can only use strings as keys (anything else is converted to a string).
You could, alternatively, maintain an array which indexes the objects in question, and use its index string as a reference to the object. Something like this:
var ObjectReference = [];
ObjectReference.push(obj);
set['ObjectReference.' + ObjectReference.indexOf(obj)] = true;
Obviously it's a little verbose, but you could write a couple of methods that handle it and get and set all willy nilly.
Edit:
Your guess is fact -- this is defined behaviour in JavaScript -- specifically a toString conversion occurs meaning that you can can define your own toString function on the object that will be used as the property name. - olliej
This brings up another interesting point; you can define a toString method on the objects you want to hash, and that can form their hash identifier.
The easiest way to do this is to give each of your objects its own unique toString method:
(function() {
var id = 0;
/*global MyObject */
MyObject = function() {
this.objectId = '<#MyObject:' + (id++) + '>';
this.toString= function() {
return this.objectId;
};
};
})();
I had the same problem and this solved it perfectly for me with minimal fuss, and was a lot easier that re-implementing some fatty Java style Hashtable and adding equals() and hashCode() to your object classes. Just make sure that you don't also stick a string '<#MyObject:12> into your hash or it will wipe out the entry for your exiting object with that id.
Now all my hashes are totally chill. I also just posted a blog entry a few days ago about this exact topic.
What you described is covered by Harmony WeakMaps, part of the ECMAScript 6 specification (next version of JavaScript). That is: a set where the keys can be anything (including undefined) and is non-enumerable.
This means it's impossible to get a reference to a value unless you have a direct reference to the key (any object!) that links to it. It's important for a bunch of engine implementation reasons relating to efficiency and garbage collection, but it's also super cool for in that it allows for new semantics like revokable access permissions and passing data without exposing the data sender.
From MDN:
var wm1 = new WeakMap(),
wm2 = new WeakMap();
var o1 = {},
o2 = function(){},
o3 = window;
wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // A value can be anything, including an object or a function.
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // Keys and values can be any objects. Even WeakMaps!
wm1.get(o2); // "azerty"
wm2.get(o2); // Undefined, because there is no value for o2 on wm2.
wm2.get(o3); // Undefined, because that is the set value.
wm1.has(o2); // True
wm2.has(o2); // False
wm2.has(o3); // True (even if the value itself is 'undefined').
wm1.has(o1); // True
wm1.delete(o1);
wm1.has(o1); // False
WeakMaps are available in current Firefox, Chrome and Edge. They're also supported in Node v7 , and in v6 with the --harmony-weak-maps flag.
The solution I chose is similar to Daniel's, but rather than use an object factory and override the toString, I explicitly add the hash to the object when it is first requested through a getHashCode function. A little messy, but better for my needs :)
Function.prototype.getHashCode = (function(id) {
return function() {
if (!this.hashCode) {
this.hashCode = '<hash|#' + (id++) + '>';
}
return this.hashCode;
}
}(0));
For my specific situation I only care about the equality of the object as far as keys and primitive values go. The solution that worked for me was converting the object to its JSON representation and using that as the hash. There are limitations such as order of key definition potentially being inconsistent; but like I said it worked for me because these objects were all being generated in one place.
var hashtable = {};
var myObject = {a:0,b:1,c:2};
var hash = JSON.stringify(myObject);
// '{"a":0,"b":1,"c":2}'
hashtable[hash] = myObject;
// {
// '{"a":0,"b":1,"c":2}': myObject
// }
I put together a small JavaScript module a while ago to produce hashcodes for strings, objects, arrays, etc. (I just committed it to GitHub :) )
Usage:
Hashcode.value("stackoverflow")
// -2559914341
Hashcode.value({ 'site' : "stackoverflow" })
// -3579752159
In ECMAScript 6 there's now a Set that works how you'd like: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
It's already available in the latest Chrome, FF, and IE11.
The JavaScript specification defines indexed property access as performing a toString conversion on the index name. For example,
myObject[myProperty] = ...;
is the same as
myObject[myProperty.toString()] = ...;
This is necessary as in JavaScript
myObject["someProperty"]
is the same as
myObject.someProperty
And yes, it makes me sad as well :-(
Based on the title, we can generate strong SHA hashes, in a browser context, it can be used to generate a unique hash from an object, an array of params, a string, or whatever.
async function H(m) {
const msgUint8 = new TextEncoder().encode(m)
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8)
const hashArray = Array.from(new Uint8Array(hashBuffer))
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
console.log(hashHex)
}
/* Examples ----------------------- */
H("An obscure ....")
H(JSON.stringify( {"hello" : "world"} ))
H(JSON.stringify( [54,51,54,47] ))
The above output in my browser, it should be equal for you too:
bf1cf3fe6975fe382ab392ec1dd42009380614be03d489f23601c11413cfca2b
93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588
d2f209e194045604a3b15bdfd7502898a0e848e4603c5a818bd01da69c00ad19
Supported algos:
SHA-1 (but don't use this in cryptographic applications)
SHA-256
SHA-384
SHA-512
https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#Converting_a_digest_to_a_hex_string
However, for a simple FAST checksum hash function, made only for collision avoidance, see CRC32 (Content Redundancy Check)
JavaScript CRC32
You might also be interested by this similar method to generate HMAC codes via the web crypto api.
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
you can use Es6 symbol to create unique key and access object.
Every symbol value returned from Symbol() is unique. A symbol value may be used as an identifier for object properties; this is the data type's only purpose.
var obj = {};
obj[Symbol('a')] = 'a';
obj[Symbol.for('b')] = 'b';
obj['c'] = 'c';
obj.d = 'd';
Here's my simple solution that returns a unique integer.
function hashcode(obj) {
var hc = 0;
var chars = JSON.stringify(obj).replace(/\{|\"|\}|\:|,/g, '');
var len = chars.length;
for (var i = 0; i < len; i++) {
// Bump 7 to larger prime number to increase uniqueness
hc += (chars.charCodeAt(i) * 7);
}
return hc;
}
My solution introduces a static function for the global Object object.
(function() {
var lastStorageId = 0;
this.Object.hash = function(object) {
var hash = object.__id;
if (!hash)
hash = object.__id = lastStorageId++;
return '#' + hash;
};
}());
I think this is more convenient with other object manipulating functions in JavaScript.
I will try to go a little deeper than other answers.
Even if JS had better hashing support it would not magically hash everything perfectly, in many cases you will have to define your own hash function. For example Java has good hashing support, but you still have to think and do some work.
One problem is with the term hash/hashcode ... there is cryptographic hashing and non-cryptographic hashing. The other problem, is you have to understand why hashing is useful and how it works.
When we talk about hashing in JavaScript or Java most of the time we are talking about non-cryptographic hashing, usually about hashing for hashmap/hashtable (unless we are working on authentication or passwords, which you could be doing server-side using NodeJS ...).
It depends on what data you have and what you want to achieve.
Your data has some natural "simple" uniqueness:
The hash of an integer is ... the integer, as it is unique, lucky you !
The hash of a string ... it depends on the string, if the string represents a unique identifier, you may consider it as a hash (so no hashing needed).
Anything which is indirectly pretty much a unique integer is the simplest case
This will respect: hashcode equal if objects are equal
Your data has some natural "composite" uniqueness:
For example with a person object, you may compute a hash using firstname, lastname, birthdate, ... see how Java does it: Good Hash Function for Strings, or use some other ID info that is cheap and unique enough for your usecase
You have no idea what your data will be:
Good luck ... you could serialize to string and hash it Java style, but that may be expensive if the string is large and it will not avoid collisions as well as say the hash of an integer (self).
There is no magically efficient hashing technique for unknown data, in some cases it is quite easy, in other cases you may have to think twice. So even if JavaScript/ECMAScript adds more support, there is no magic language solution for this problem.
In practice you need two things: enough uniqueness, enough speed
In addition to that it is great to have: "hashcode equal if objects are equal"
https://en.wikipedia.org/wiki/Hash_table#Collision_resolution
Relationship between hashCode and equals method in Java
I combined the answers from eyelidlessness and KimKha.
The following is an angularjs service and it supports numbers, strings, and objects.
exports.Hash = () => {
let hashFunc;
function stringHash(string, noType) {
let hashString = string;
if (!noType) {
hashString = `string${string}`;
}
var hash = 0;
for (var i = 0; i < hashString.length; i++) {
var character = hashString.charCodeAt(i);
hash = ((hash<<5)-hash)+character;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
function objectHash(obj, exclude) {
if (exclude.indexOf(obj) > -1) {
return undefined;
}
let hash = '';
const keys = Object.keys(obj).sort();
for (let index = 0; index < keys.length; index += 1) {
const key = keys[index];
const keyHash = hashFunc(key);
const attrHash = hashFunc(obj[key], exclude);
exclude.push(obj[key]);
hash += stringHash(`object${keyHash}${attrHash}`, true);
}
return stringHash(hash, true);
}
function Hash(unkType, exclude) {
let ex = exclude;
if (ex === undefined) {
ex = [];
}
if (!isNaN(unkType) && typeof unkType !== 'string') {
return unkType;
}
switch (typeof unkType) {
case 'object':
return objectHash(unkType, ex);
default:
return stringHash(String(unkType));
}
}
hashFunc = Hash;
return Hash;
};
Example Usage:
Hash('hello world'), Hash('hello world') == Hash('hello world')
Hash({hello: 'hello world'}), Hash({hello: 'hello world'}) == Hash({hello: 'hello world'})
Hash({hello: 'hello world', goodbye: 'adios amigos'}), Hash({hello: 'hello world', goodbye: 'adios amigos'}) == Hash({goodbye: 'adios amigos', hello: 'hello world'})
Hash(['hello world']), Hash(['hello world']) == Hash(['hello world'])
Hash(1), Hash(1) == Hash(1)
Hash('1'), Hash('1') == Hash('1')
Output
432700947 true
-411117486 true
1725787021 true
-1585332251 true
1 true
-1881759168 true
Explanation
As you can see the heart of the service is the hash function created by KimKha.I have added types to the strings so that the sturucture of the object would also impact the final hash value.The keys are hashed to prevent array|object collisions.
eyelidlessness object comparision is used to prevent infinit recursion by self referencing objects.
Usage
I created this service so that I could have an error service that is accessed with objects. So that one service can register an error with a given object and another can determine if any errors were found.
ie
JsonValidation.js
ErrorSvc({id: 1, json: '{attr: "not-valid"}'}, 'Invalid Json Syntax - key not double quoted');
UserOfData.js
ErrorSvc({id: 1, json: '{attr: "not-valid"}'});
This would return:
['Invalid Json Syntax - key not double quoted']
While
ErrorSvc({id: 1, json: '{"attr": "not-valid"}'});
This would return
[]
If you truly want set behavior (I'm going by Java knowledge), then you will be hard pressed to find a solution in JavaScript. Most developers will recommend a unique key to represent each object, but this is unlike set, in that you can get two identical objects each with a unique key. The Java API does the work of checking for duplicate values by comparing hash code values, not keys, and since there is no hash code value representation of objects in JavaScript, it becomes almost impossible to do the same. Even the Prototype JS library admits this shortcoming, when it says:
"Hash can be thought of as an
associative array, binding unique keys
to values (which are not necessarily
unique)..."
http://www.prototypejs.org/api/hash
In addition to eyelidlessness's answer, here is a function that returns a reproducible, unique ID for any object:
var uniqueIdList = [];
function getConstantUniqueIdFor(element) {
// HACK, using a list results in O(n), but how do we hash e.g. a DOM node?
if (uniqueIdList.indexOf(element) < 0) {
uniqueIdList.push(element);
}
return uniqueIdList.indexOf(element);
}
As you can see it uses a list for look-up which is very inefficient, however that's the best I could find for now.
If you want to use objects as keys you need to overwrite their toString Method, as some already mentioned here. The hash functions that were used are all fine, but they only work for the same objects not for equal objects.
I've written a small library that creates hashes from objects, which you can easily use for this purpose. The objects can even have a different order, the hashes will be the same. Internally you can use different types for your hash (djb2, md5, sha1, sha256, sha512, ripemd160).
Here is a small example from the documentation:
var hash = require('es-hash');
// Save data in an object with an object as a key
Object.prototype.toString = function () {
return '[object Object #'+hash(this)+']';
}
var foo = {};
foo[{bar: 'foo'}] = 'foo';
/*
* Output:
* foo
* undefined
*/
console.log(foo[{bar: 'foo'}]);
console.log(foo[{}]);
The package can be used either in browser and in Node-Js.
Repository: https://bitbucket.org/tehrengruber/es-js-hash
If you want to have unique values in a lookup object you can do something like this:
Creating a lookup object
var lookup = {};
Setting up the hashcode function
function getHashCode(obj) {
var hashCode = '';
if (typeof obj !== 'object')
return hashCode + obj;
for (var prop in obj) // No hasOwnProperty needed
hashCode += prop + getHashCode(obj[prop]); // Add key + value to the result string
return hashCode;
}
Object
var key = getHashCode({ 1: 3, 3: 7 });
// key = '1337'
lookup[key] = true;
Array
var key = getHashCode([1, 3, 3, 7]);
// key = '01132337'
lookup[key] = true;
Other types
var key = getHashCode('StackOverflow');
// key = 'StackOverflow'
lookup[key] = true;
Final result
{ 1337: true, 01132337: true, StackOverflow: true }
Do note that getHashCode doesn't return any value when the object or array is empty
getHashCode([{},{},{}]);
// '012'
getHashCode([[],[],[]]);
// '012'
This is similar to #ijmacd solution only getHashCode doesn't has the JSON dependency.
Just use hidden secret property with the defineProperty enumerable: false
It work very fast:
The first read uniqueId: 1,257,500 ops/s
All others: 309,226,485 ops/s
var nextObjectId = 1
function getNextObjectId() {
return nextObjectId++
}
var UNIQUE_ID_PROPERTY_NAME = '458d576952bc489ab45e98ac7f296fd9'
function getObjectUniqueId(object) {
if (object == null) {
return null
}
var id = object[UNIQUE_ID_PROPERTY_NAME]
if (id != null) {
return id
}
if (Object.isFrozen(object)) {
return null
}
var uniqueId = getNextObjectId()
Object.defineProperty(object, UNIQUE_ID_PROPERTY_NAME, {
enumerable: false,
configurable: false,
writable: false,
value: uniqueId,
})
return uniqueId
}

Categories