This question already has answers here:
How can I print a circular structure in a JSON-like format?
(31 answers)
Closed 10 days ago.
In order to prevent that error regarding circular structure to JSON I have this function:
circular = () => { //fix circular stuff for json.stringify
seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
};
it works fairly well except in the case below:
var gameon = 1;
var fighter1 = {"userid":"97","username":"john","items":{},"ailments":{}};
var fighter2 = {"userid":"91","username":"james","items":{},"ailments":{}};
var resume = 30;
all = {"gameon":gameon,"fighter1":fighter1,"fighter2":fighter2,"resume":resume,"inturn":fighter1,"outturn":fighter2};
console.log(JSON.stringify(all,circular()));
it will print something like this:
{"gameon":1,"fighter1":{"userid":"97","username":"john","items":{},"ailments":{}},"fighter2":{"userid":"91","username":"james","items":{},"ailments":{}},"resume":30}"
please notice how the output truncates after resume.
If I dont use the circular function then I get the correct output but also the "circular structure" error if I try to write to a file.
Why is that?
Thank you.
Your circular function returns undefined for any already seen objects. JSON.stringify() comes across undefined and it stops processing the rest of the object. You can try to modify your function to add a placeholder for circular references:
const circular = () => {
let seen = new WeakSet();
let placeholder = {};
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return placeholder;
}
seen.add(value);
}
return value;
};
};
I want to do:
properties.email.value without triggering an error like: Can't read 'value' of 'undefined'
However, I don't want to do:
properties.email && properties.email.value and I don't want to use an helper, something like: get(properties, 'email.value').
I really want to keep the syntax properties.email.value
I can solve this by doing:
Object.defineProperty(properties, 'email', {
get: () => properties.email && properties.email.value,
enumerable: true,
configurable: true
});
Now the getter is in charge of doing my safety check. Perfect.
But I also want to be able to do properties.name.value safely.
But as the properties object comes from the API (json), I don't know the full list of properties possible.
So, is there a way to use this "magical" get syntax for any prop access like: properties[ANYTHING].value ?
OK, I've got something like this.
But you must create properties that way.
Hope this help :)
var properties = {
phone : {
value: "123456789"
}
}
var handler = {
get: function(target, name) {
return target.hasOwnProperty(name) ? target[name] : {};
}
};
var new_properties = new Proxy(properties, handler);
console.log("phone.value = " + new_properties.phone.value);
console.log("email.value = " + new_properties.email.value);
new_properties.email = {
value: 1
};
console.log("email.value after assign = " + new_properties.email.value);
The document reference here.
Edited
Even if the original properties object is unknown, this kind of usage works as well.
You could use a Proxy and get known properties and a custom result for unknow properties.
For changing properties, you could take the same approach and set the value.
var properties = { email: { value: 'foo#example.com' } },
proxy = new Proxy(
properties,
{
get: function(target, prop, receiver) {
if (prop in target) {
return target[prop] && target[prop].value
} else {
return;
}
},
set: function(target, prop, value) {
if (prop in target) {
target[prop].value = value;
} else {
target[prop] = { value };
}
}
}
);
console.log(proxy.email);
console.log(proxy.bar);
proxy.email = '41';
console.log(proxy.email);
I can't believe I'm doing this...
var wordlength = 7;
var alphabet="abcdefghijklmnopqrstuvwxyz";
alphabet += alphabet.toUpperCase() + "0123456789_";
var alen = alphabet.length;
var buildWord = function(number){
if(number===0){
return '';
}
return alphabet[number%alen]+buildWord(Math.floor(number/alen));
};
var total = Math.pow(alen, wordlength);
for(var i = 1; i<total; i++){
var w = buildWord(i);
if(isNaN(w[0]) && Object.prototype[w]===undefined){
Object.prototype[w]={};
}
}
If I have a JavaScript object:
var object = {
propertyOne: undefined,
propertyTwo: 'defined',
propertyThree: 'defined',
propertyFour: undefined
}
How can I create a method that will list the properties with undefined values (in the example's case, propertyOne and propertyFour).
I'm quite new to JavaScript, and this is what I had so far:
function getEmptyProperties(object) {
var emptyProps = [];
for (var property in object) {
if (object.property === undefined) {
emptyProps += property
}
}
return emptyProps
}
But this returns ALL of the properties, regardless if they are undefined or not.
I know I'm missing some things that are principal in JS, but can't figure it out. Kindly help please?
Iterate over the object with:
Object.keys(object).forEach(function(val, i){
if (object[val] === undefined){
//do things to save the properties you want to save or delete
}
})
For fundamentals I might suggest Eloquent JavaScript. It's free.
You have a number of syntax errors in your code that could be fixed if you read the first 6ish chapters.
var getEmptyProperties = function(object) {
var emptyProps = [];
for (var property in object) {
if (object[property] === undefined) {
emptyProps.push(property);
}
}
return emptyProps
}
You may use filter, to get an array of only the properties that are undefined:
var obj = {
propertyOne: undefined,
propertyTwo: 'defined',
propertyThree: 'defined',
propertyFour: undefined
};
var undefProps = Object.keys(obj).filter(function (key) {
return obj[key] === undefined;
});
// undefProps: ["propertyOne", "propertyFour"]
And with an arrow function!:
var undefProps = Object.keys(obj).filter(k => obj[k] === undefined);
The title is pretty self-explanatory..
Is there a way to read whatever's been output to the console.log up until the moment you decide to read it, using Javascript?
You can make a proxy around it, such as :
(function(win){
var ncon = win.console;
var con = win.console = {
backlog: []
};
for(var k in ncon) {
if(typeof ncon[k] === 'function') {
con[k] = (function(fn) {
return function() {
con.backlog.push([new Date(), fn, arguments]);
ncon[fn].apply(ncon, arguments);
};
})(k);
}
}
})(window);
I am looking for an efficient way to translate my Ember object to a json string, to use it in a websocket message below
/*
* Model
*/
App.node = Ember.Object.extend({
name: 'theName',
type: 'theType',
value: 'theValue',
})
The websocket method:
App.io.emit('node', {node: hash});
hash should be the json representation of the node. {name: thename, type: theType, ..}
There must be a fast onliner to do this.. I dont want to do it manualy since i have many attributes and they are likely to change..
As stated you can take inspiration from the ember-runtime/lib/core.js#inspect function to get the keys of an object, see http://jsfiddle.net/pangratz666/UUusD/
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, ret = [];
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === 'toString') {
continue;
} // ignore useless items
if (Ember.typeOf(v) === 'function') {
continue;
}
ret.push(key);
}
}
return this.getProperties.apply(this, ret);
}
});
Note, since commit 1124005 - which is available in ember-latest.js and in the next release - you can pass the ret array directly to getProperties, so the return statement of the getJson function looks like this:
return this.getProperties(ret);
You can get a plain JS object (or hash) from an Ember.Object instance by calling getProperties() with a list of keys.
If you want it as a string, you can use JSON.stringify().
For example:
var obj = Ember.Object.create({firstName: 'Erik', lastName: 'Bryn', login: 'ebryn'}),
hash = obj.getProperties('firstName', 'lastName'), // => {firstName: 'Erik', lastName: 'Bryn'}
stringHash = JSON.stringify(hash); // => '{"firstName": "Erik", "lastName": "Bryn"}'
I have also been struggling with this. As Mirko says, if you pass the ember object to JSON.stringify you will get circular reference error. However if you store the object inside one property and use stringify on that object, it works, even nested subproperties.
var node = Ember.Object.create({
data: {
name: 'theName',
type: 'theType',
value: 'theValue'
}
});
console.log(JSON.stringify(node.get('data')));
However, this only works in Chrome, Safari and Firefox. In IE8 I get a stack overflow so this isn't a viable solution.
I have resorted to creating JSON schemas over my object models and written a recursive function to iterate over the objects using the properties in the schemas and then construct pure Javascript objects which I can then stringify and send to my server. I also use the schemas for validation so this solution works pretty well for me but if you have very large and dynamic data models this isn't possible. I'm also interested in simpler ways to accomplish this.
I modifed #pangratz solution slightly to make it handle nested hierarchies of Jsonables:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {};
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === 'toString') {
continue;
}
if (Ember.typeOf(v) === 'function') {
continue;
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
App.io.emit('node', {node: node.toJSON()});
Or if you have an ID property and want to include it:
App.io.emit('node', {node: node.toJSON({includeId: true})});
Will this work for you?
var json = JSON.stringify( Ember.getMeta( App.node, 'values') );
The false is optional, but would be more performant if you do not intend to modify any of the properties, which is the case according to your question. This works for me, but I am wary that Ember.meta is a private method and may work differently or not even be available in future releases. (Although, it isn't immediately clear to me if Ember.getMeta() is private). You can view it in its latest source form here:
https://github.com/emberjs/ember.js/blob/master/packages/ember-metal/lib/utils.js
The values property contains only 'normal' properties. You can collect any cached, computed properties from Ember.meta( App.node, false ).cached. So, provided you use jQuery with your build, you can easily merge these two objects like so:
$.extend( {}, Ember.getMeta(App.node, 'values'), Ember.getMeta(App.node, 'cache') );
Sadly, I haven't found a way to get sub-structures like array properties in this manner.
I've written an extensive article on how you can convert ember models into native objects or JSON which may help you or others :)
http://pixelchild.com.au/post/44614363941/how-to-convert-ember-objects-to-json
http://byronsalau.com/blog/convert-ember-objects-to-json/
I modified #Kevin-pauli solution to make it works with arrays as well:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {}, inspectArray = function (aSome) {
if (Ember.typeof(aSome) === 'array') {
return aSome.map(inspectArray);
}
if (Jsonable.detect(aSome)) {
return aSome.getJson();
}
return aSome;
};
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === 'toString') {
continue;
}
if (Ember.typeOf(v) === 'function') {
continue;
}
if (Ember.typeOf(v) === 'array') {
v = v.map(inspectArray);
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
I also made some further modification to get the best of both worlds. With the following version I check if the Jsonable object has a specific property that informs me on which of its properties should be serialized:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {}, base, inspectArray = function (aSome) {
if (Ember.typeof(aSome) === 'array') {
return aSome.map(inspectArray);
}
if (Jsonable.detect(aSome)) {
return aSome.getJson();
}
return aSome;
};
if (!Ember.isNone(this.get('jsonProperties'))) {
// the object has a selective list of properties to inspect
base = this.getProperties(this.get('jsonProperties'));
} else {
// no list given: let's use all the properties
base = this;
}
for (var key in base) {
if (base.hasOwnProperty(key)) {
v = base[key];
if (v === 'toString') {
continue;
}
if (Ember.typeOf(v) === 'function') {
continue;
}
if (Ember.typeOf(v) === 'array') {
v = v.map(inspectArray);
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
I am using this little tweak and I am happy with it. I hope it'll help others as well!
Thanks to #pangratz and #Kevin-Pauli for their solution!
Here I take #leo, #pangratz and #kevin-pauli solution a little step further. Now it iterates not only with arrays but also through has many relationships, it doesn't check if a value has the type Array but it calls the isArray function defined in Ember's API.
Coffeescript
App.Jsonable = Em.Mixin.create
getJson: ->
jsonValue = (attr) ->
return attr.map(jsonValue) if Em.isArray(attr)
return attr.getJson() if App.Jsonable.detect(attr)
attr
base =
if Em.isNone(#get('jsonProperties'))
# no list given: let's use all the properties
this
else
# the object has a selective list of properties to inspect
#getProperties(#get('jsonProperties'))
hash = {}
for own key, value of base
continue if value is 'toString' or Em.typeOf(value) is 'function'
json[key] = jsonValue(value)
json
Javascript
var hasProp = {}.hasOwnProperty;
App.Jsonable = Em.Mixin.create({
getJson: function() {
var base, hash, hashValue, key, value;
jsonValue = function(attr) {
if (Em.isArray(attr)) {
return attr.map(jsonValue);
}
if (App.Jsonable.detect(attr)) {
return attr.getJson();
}
return attr;
};
base = Em.isNone(this.get('jsonProperties')) ? this : this.getProperties(this.get('jsonProperties'));
json = {};
for (key in base) {
if (!hasProp.call(base, key)) continue;
value = base[key];
if (value === 'toString' || Em.typeOf(value) === 'function') {
continue;
}
json[key] = jsonValue(value);
}
return json;
}
});
Ember Data Model's object counts with a toJSON method which optionally receives an plain object with includeId property used to convert an Ember Data Model into a JSON with the properties of the model.
https://api.emberjs.com/ember-data/2.10/classes/DS.Model/methods/toJSON?anchor=toJSON
You can use it as follows:
const objects = models.map((model) => model.toJSON({ includeId: true }));
Hope it helps. Enjoy!
I have:
fixed and simplified code
added circular reference prevention
added use of get of value
removed all of the default properties of an empty component
//Modified by Shimon Doodkin
//Based on answers of: #leo, #pangratz, #kevin-pauli, #Klaus
//http://stackoverflow.com/questions/8669340
App.Jsonable = Em.Mixin.create({
getJson : function (keysToSkip, visited) {
//getJson() called with no arguments,
// they are to pass on values during recursion.
if (!keysToSkip)
keysToSkip = Object.keys(Ember.Component.create());
if (!visited)
visited = [];
visited.push(this);
var getIsFunction;
var jsonValue = function (attr, key, obj) {
if (Em.isArray(attr))
return attr.map(jsonValue);
if (App.Jsonable.detect(attr))
return attr.getJson(keysToSkip, visited);
return getIsFunction?obj.get(key):attr;
};
var base;
if (!Em.isNone(this.get('jsonProperties')))
base = this.getProperties(this.get('jsonProperties'));
else
base = this;
getIsFunction=Em.typeOf(base.get) === 'function';
var json = {};
var hasProp = Object.prototype.hasOwnProperty;
for (var key in base) {
if (!hasProp.call(base, key) || keysToSkip.indexOf(key) != -1)
continue;
var value = base[key];
// there are usual circular references
// on keys: ownerView, controller, context === base
if ( value === base ||
value === 'toString' ||
Em.typeOf(value) === 'function')
continue;
// optional, works also without this,
// the rule above if value === base covers the usual case
if (visited.indexOf(value) != -1)
continue;
json[key] = jsonValue(value, key, base);
}
visited.pop();
return json;
}
});
/*
example:
DeliveryInfoInput = Ember.Object.extend(App.Jsonable,{
jsonProperties: ["title","value","name"], //Optionally specify properties for json
title:"",
value:"",
input:false,
textarea:false,
size:22,
rows:"",
name:"",
hint:""
})
*/
Ember.js appears to have a JSON library available. I hopped into a console (Firebug) on one the Todos example and the following worked for me:
hash = { test:4 }
JSON.stringify(hash)
So you should be able to just change your line to
App.io.emit('node', { node:JSON.stringify(hash) })