All object property is not empty javascript - javascript

How to check the following object, make sure every property is not equal to undefined, null or empty string?
userObj = {
name: {
first: req.body.first,
last: req.body.last
},
location: {
city: req.body.city
},
phone: req.body.phone
}
I can check req.body one by one like if(req.body.first) but that's too tedious if I have many params.

You can simply use Array.prototype.some() method over Object.values() to implement this in a recursive way:
function isThereAnUndefinedValue(obj) {
return Object.values(obj).some(function(v) {
if (typeof v === 'object'){
if(v.length && v.length>0){
return v.some(function(el){
return (typeof el === "object") ? isThereAnUndefinedValue(el) : (el!==0 && el!==false) ? !el : false;
});
}
return isThereAnUndefinedValue(v);
}else {
console.log(v);
return (v!==0 && v!==false) ? !v : false;
}
});
}
In the following function we will:
Loop over our object values.
Check if the iterated value is an object we call the function recursively with this value.
Otherwise we will just check if this is a truthy value or not.
Demo:
This is a working Demo:
userObj = {
name: {
first: "req.body.first",
last: [5, 10, 0, 40]
},
location: {
city: "req.body.city"
},
phone: "req.body.phone"
}
function isThereAnUndefinedValue(obj) {
return Object.values(obj).some(function(v) {
if (typeof v === 'object'){
if(v.length && v.length>0){
return v.some(function(el){
return (typeof el === "object") ? isThereAnUndefinedValue(el) : (el!==0 && el!==false) ? !el : false;
});
}
return isThereAnUndefinedValue(v);
}else {
console.log(v);
return (v!==0 && v!==false) ? !v : false;
}
});
}
console.log(isThereAnUndefinedValue(userObj));
You will get a function that validates every object and its sub objects in a recursive way.

To check for truthy values (values other than false, '', 0, null, undefined):
const hasOnlyTruthyValues = obj => Object.values(obj).every(Boolean);
Example:
const hasOnlyTruthyValues = obj => Object.values(obj).every(Boolean);
const object = {
a: '12',
b: 12,
c: ''
};
console.log(hasOnlyTruthyValues(object));
The example you posted (if (res.body.first) ...) also checks for a truthy value. If you want to allow false and 0 (which are falsy, but you didn't mention them in your question), use:
const hasOnlyAllowedValues = obj => Object.values(obj).every(v => v || v === 0 || v === false);

you can create a simple validator for yourself like function below
var body = {
a: 1,
b: 3
};
var keys = ['a', 'b', 'c'];
function validate(body, keys) {
for (var i in keys) {
if (!body[keys[i]]) {
return false;
}
}
return true;
}
console.log(validate(body, keys));

you can use the following validator to check, it works for a nested object as well
function objectValidator(obj){
let ret = true;
for(property in obj){
//console.log(property, obj[property], typeof obj[property]);
if(typeof obj[property] == "object"){
if(obj[property] instanceof Array){
ret = (ret & true);
}
else if(obj[property] == null){
ret = false;
}
else{
ret = (ret & objectValidator(obj[property]));
}
}
else if(typeof obj[property] == "string"){
if(obj[property] == ""){
ret = false;
}
}
else if(typeof obj[property] == "undefined"){
ret = false;
}
}
return ret;
}
let a = {
b : 1,
c :{
d: 2,
e: [3, 4],
f : ""
}
}
console.log(objectValidator(a));

In your post you use:
if(req.body.first)
I don't know if you are checking req and body before hand, but this should be:
if ( typeof req == "object" && typeof req.body == "object"
&& req.body.first ) {
That is a safer way to check before use.
Or, if you want to embed in the object:
function fnTest(a,b,c) {
return (typeof a == "object" && typeof b == "object" && c) ? c : "";
};
userObj = {name:{
first: fnTest(req, req.body, req.body.first)
,last: fnTest(req,req.body, req.body.last)
},location:{
city: fnTest(req,req.body,req.body.city)
},phone: fnTest(req.body.phone)
};

Related

Dig till deepest .value or .content

I am trying to write a function that digs an object till it gets to the last .value or .content property. I wrote this and for the life of me I cant figure out why it breaks.
var jscGetDeepest = function(obj) {
try {
console.info(Math.round(Math.random() * 10) + ' starting jscGetDeepest:', obj, obj.toString());
} catch(ignore) {}
while (obj && ('contents' in obj || 'value' in obj)) {
if ('contents' in obj) {
obj = obj.contents;
} else if ('value' in obj) {
obj = obj.value;
}
//console.info('loop jscGetDeepest:', obj.toString());
}
if (obj || obj === 0) {
obj = obj.toString();
}
console.info('finaled jscGetDeepest:', obj);
return obj;
}
The issue occurs when inner value in the next iteration is not an object. In this case you get an error message, because in operand can't be used with primitives.
To fix it check for object before trying to get deeper. Here is a fixed an slightly improved version with JSON.stringify instead of toString (maybe better to return object itself without stringifying it?):
var jscGetDeepest = function (obj) {
while (typeof obj === 'object' && obj !== null && ('contents' in obj || 'value' in obj)) {
if ('contents' in obj) {
obj = obj.contents;
} else if ('value' in obj) {
obj = obj.value;
}
}
if (typeof obj === 'object') {
obj = JSON.stringify(obj);
}
return obj;
}
alert( jscGetDeepest({value: {name: 2, contents: {name: 3, value: 23}}}) );
alert( jscGetDeepest({value: {name: 2, value: {name: 3, contents: {name: 4}}}}) );

Updating Javascript object recursively

I have an nested object that I want to update it with values provided by object that contains similar structure but only the properties that I want updated. Creating a new result instead of modifying the initial objects is great too.
var initial =
{
a: 1,
b : {
c : 2,
d : 3
},
f: 5
};
var update = {
a: 2,
b: {
d: 2
}
};
function updateFunction (a,b) { return a+b;};
var result=
{
a: 3, // updateFunction (1,2)=> 3
b : {
c : 2,
d :5 // updateFunction (3,2) => 5
},
f: 5
};
Have not tested fully, but maybe,
assuming objects are simple as stated,
function updateFunction (a,b) { return a + b;};
function recurse(initial, update){
for(prop in initial){
if({}.hasOwnProperty.call(initial, prop) && {}.hasOwnProperty.call(update, prop)){
if(typeof initial[prop] === 'object' && typeof update[prop] === 'object'){
recurse(initial[prop], update[prop]);
}
else{
initial[prop] = updateFunction(initial[prop], update[prop]);
}
}
}
}
recurse(initial, update);
EDIT
If result is expected without changing initial
function updateFunction (a,b) { return a + b;};
function recurse(initial, update){
var result = {};
for(prop in initial){
if({}.hasOwnProperty.call(initial, prop)){
result[prop] = initial[prop];
if({}.hasOwnProperty.call(update, prop)){
if(typeof initial[prop] === 'object' && typeof update[prop] === 'object'){
result[prop] = recurse(initial[prop], update[prop]);
}
else{
result[prop] = updateFunction(initial[prop], update[prop]);
}
}
}
}
return result;
}
var result = recurse(initial, update);
hope this helps.
Here's how I'd do it:
// The parallel to Array.map
Object.map = function(obj, f) {
var result = {};
for(k in obj)
if({}.hasOwnProperty.call(obj, k))
result[k] = f(k, obj[k]);
return result;
}
// Takes two objects and uses `resolve` to merge them
function merge(a, b, resolve) {
return Object.map(a, function(k, a_value) {
if(k in b)
return resolve(a_value, b[k]);
else
return a_value;
});
}
// same as above, but recursing when an object is found
function recursive_merge(a, b, resolve) {
return merge(a, b, function(a_value, b_value) {
if(typeof a_value == 'object' && typeof b_value == 'object')
return recursive_merge(a_value, b_value, resolve);
else
return resolve(a_value, b_value);
});
}
result = recursive_merge(initial, update, function(a, b) { return a + b; })

finding property in js object by name

I've got many objects that structures aren't the same. But I know that all of them have got property name 'siteName'. My question is how can I get value from this property.
Explame of few objects:
feature1 = {
display: "name",
feature: {
attributes: {
when: '111',
what: '222'
},
geometry: null
infoTemplate: undefined
},
symbol: null
siteName: 'aa'
}
feature2 = {
feature: {
attributes: {
when: '111',
what: '222'
},
geometry: null
infoTemplate: undefined
},
static: {
format: {
weight: 12,
siteName: 'cccc'
},
}
}
Here's a recursive function that should work for you.
It returns the value of the first property found with the name, otherwise returns undefined.
function findByName(obj, prop) {
for (var p in obj) {
if (p === prop) {
return obj[p];
} else if (obj[p] && typeof obj[p] === "object") {
var result = findByName(obj[p], prop);
if (result !== undefined)
return result;
}
}
}
var result = findByName(myObject, "siteName");
Or here's another variation that avoids inherited properties.
function findByName(obj, prop) {
if (obj.hasOwnProperty(prop))
return obj[prop];
for (var p in obj) {
if (obj[p] && typeof obj[p] === "object") {
var result = findByName(obj[p], prop);
if (result !== undefined)
return result;
}
}
}
Recursively loop through the objects:
function find(obj, name) {
for (var k in obj) { // Loop through all properties of the object.
if(k == name){ // If the property is the one you're looking for.
return obj[k]; // Return it.
}else if (typeof obj[k] == "object"){ // Else, if the object at [key] is a object,
var t = find(obj[k], name); // Loop through it.
if(t){ // If the recursive function did return something.
return t; // Return it to the higher recursion iteration, or to the first function call.
}
}
}
}
Usage:
find(feature1, "siteName"); //Returns "aa"
The following function should suit your needs:
function getFirstFoundPropertyValue(searchedKey, object) {
if(typeof object === "object") {
for (var key in object) {
var currentValue = object[key];
if(key === searchedKey) {
return currentValue;
}
var nested = getFirstFoundPropertyValue(searchedKey, currentValue);
if(typeof nested !== "undefined") {
return nested;
}
}
}
}
It returns the value of the key if the key is found, undefined otherwise. If the key appears several times, the first found one will be returned.

How to check if JavaScript object is JSON

I have a nested JSON object that I need to loop through, and the value of each key could be a String, JSON array or another JSON object. Depending on the type of object, I need to carry out different operations. Is there any way I can check the type of the object to see if it is a String, JSON object or JSON array?
I tried using typeof and instanceof but both didn't seem to work, as typeof will return an object for both JSON object and array, and instanceof gives an error when I do obj instanceof JSON.
To be more specific, after parsing the JSON into a JS object, is there any way I can check if it is a normal string, or an object with keys and values (from a JSON object), or an array (from a JSON array)?
For example:
JSON
var data = "{'hi':
{'hello':
['hi1','hi2']
},
'hey':'words'
}";
Sample JavaScript
var jsonObj = JSON.parse(data);
var path = ["hi","hello"];
function check(jsonObj, path) {
var parent = jsonObj;
for (var i = 0; i < path.length-1; i++) {
var key = path[i];
if (parent != undefined) {
parent = parent[key];
}
}
if (parent != undefined) {
var endLength = path.length - 1;
var child = parent[path[endLength]];
//if child is a string, add some text
//if child is an object, edit the key/value
//if child is an array, add a new element
//if child does not exist, add a new key/value
}
}
How do I carry out the object checking as shown above?
I'd check the constructor attribute.
e.g.
var stringConstructor = "test".constructor;
var arrayConstructor = [].constructor;
var objectConstructor = ({}).constructor;
function whatIsIt(object) {
if (object === null) {
return "null";
}
if (object === undefined) {
return "undefined";
}
if (object.constructor === stringConstructor) {
return "String";
}
if (object.constructor === arrayConstructor) {
return "Array";
}
if (object.constructor === objectConstructor) {
return "Object";
}
{
return "don't know";
}
}
var testSubjects = ["string", [1,2,3], {foo: "bar"}, 4];
for (var i=0, len = testSubjects.length; i < len; i++) {
alert(whatIsIt(testSubjects[i]));
}
Edit: Added a null check and an undefined check.
You can use Array.isArray to check for arrays. Then typeof obj == 'string', and typeof obj == 'object'.
var s = 'a string', a = [], o = {}, i = 5;
function getType(p) {
if (Array.isArray(p)) return 'array';
else if (typeof p == 'string') return 'string';
else if (p != null && typeof p == 'object') return 'object';
else return 'other';
}
console.log("'s' is " + getType(s));
console.log("'a' is " + getType(a));
console.log("'o' is " + getType(o));
console.log("'i' is " + getType(i));
's' is string'a' is array 'o' is object'i' is other
An JSON object is an object. To check whether a type is an object type, evaluate the constructor property.
function isObject(obj)
{
return obj !== undefined && obj !== null && obj.constructor == Object;
}
The same applies to all other types:
function isArray(obj)
{
return obj !== undefined && obj !== null && obj.constructor == Array;
}
function isBoolean(obj)
{
return obj !== undefined && obj !== null && obj.constructor == Boolean;
}
function isFunction(obj)
{
return obj !== undefined && obj !== null && obj.constructor == Function;
}
function isNumber(obj)
{
return obj !== undefined && obj !== null && obj.constructor == Number;
}
function isString(obj)
{
return obj !== undefined && obj !== null && obj.constructor == String;
}
function isInstanced(obj)
{
if(obj === undefined || obj === null) { return false; }
if(isArray(obj)) { return false; }
if(isBoolean(obj)) { return false; }
if(isFunction(obj)) { return false; }
if(isNumber(obj)) { return false; }
if(isObject(obj)) { return false; }
if(isString(obj)) { return false; }
return true;
}
you can also try to parse the data and then check if you got object:
try {
var testIfJson = JSON.parse(data);
if (typeof testIfJson == "object"){
//Json
} else {
//Not Json
}
}
catch {
return false;
}
If you are trying to check the type of an object after you parse a JSON string, I suggest checking the constructor attribute:
obj.constructor == Array || obj.constructor == String || obj.constructor == Object
This will be a much faster check than typeof or instanceof.
If a JSON library does not return objects constructed with these functions, I would be very suspiciouse of it.
The answer by #PeterWilkinson didn't work for me because a constructor for a "typed" object is customized to the name of that object. I had to work with typeof
function isJson(obj) {
var t = typeof obj;
return ['boolean', 'number', 'string', 'symbol', 'function'].indexOf(t) == -1;
}
You could make your own constructor for JSON parsing:
var JSONObj = function(obj) { $.extend(this, JSON.parse(obj)); }
var test = new JSONObj('{"a": "apple"}');
//{a: "apple"}
Then check instanceof to see if it needed parsing originally
test instanceof JSONObj
I wrote an npm module to solve this problem. It's available here:
object-types: a module for finding what literal types underly objects
Install
npm install --save object-types
Usage
const objectTypes = require('object-types');
objectTypes({});
//=> 'object'
objectTypes([]);
//=> 'array'
objectTypes(new Object(true));
//=> 'boolean'
Take a look, it should solve your exact problem. Let me know if you have any questions! https://github.com/dawsonbotsford/object-types
Why not check Number - a bit shorter and works in IE/Chrome/FF/node.js
function whatIsIt(object) {
if (object === null) {
return "null";
}
else if (object === undefined) {
return "undefined";
}
if (object.constructor.name) {
return object.constructor.name;
}
else { // last chance 4 IE: "\nfunction Number() {\n [native code]\n}\n" / node.js: "function String() { [native code] }"
var name = object.constructor.toString().split(' ');
if (name && name.length > 1) {
name = name[1];
return name.substr(0, name.indexOf('('));
}
else { // unreachable now(?)
return "don't know";
}
}
}
var testSubjects = ["string", [1,2,3], {foo: "bar"}, 4];
// Test all options
console.log(whatIsIt(null));
console.log(whatIsIt());
for (var i=0, len = testSubjects.length; i < len; i++) {
console.log(whatIsIt(testSubjects[i]));
}
I combine the typeof operator with a check of the constructor attribute (by Peter):
var typeOf = function(object) {
var firstShot = typeof object;
if (firstShot !== 'object') {
return firstShot;
}
else if (object.constructor === [].constructor) {
return 'array';
}
else if (object.constructor === {}.constructor) {
return 'object';
}
else if (object === null) {
return 'null';
}
else {
return 'don\'t know';
}
}
// Test
var testSubjects = [true, false, 1, 2.3, 'string', [4,5,6], {foo: 'bar'}, null, undefined];
console.log(['typeOf()', 'input parameter'].join('\t'))
console.log(new Array(28).join('-'));
testSubjects.map(function(testSubject){
console.log([typeOf(testSubject), JSON.stringify(testSubject)].join('\t\t'));
});
Result:
typeOf() input parameter
---------------------------
boolean true
boolean false
number 1
number 2.3
string "string"
array [4,5,6]
object {"foo":"bar"}
null null
undefined
I know this is a very old question with good answers. However, it seems that it's still possible to add my 2ยข to it.
Assuming that you're trying to test not a JSON object itself but a String that is formatted as a JSON (which seems to be the case in your var data), you could use the following function that returns a boolean (is or is not a 'JSON'):
function isJsonString( jsonString ) {
// This function below ('printError') can be used to print details about the error, if any.
// Please, refer to the original article (see the end of this post)
// for more details. I suppressed details to keep the code clean.
//
let printError = function(error, explicit) {
console.log(`[${explicit ? 'EXPLICIT' : 'INEXPLICIT'}] ${error.name}: ${error.message}`);
}
try {
JSON.parse( jsonString );
return true; // It's a valid JSON format
} catch (e) {
return false; // It's not a valid JSON format
}
}
Here are some examples of using the function above:
console.log('\n1 -----------------');
let j = "abc";
console.log( j, isJsonString(j) );
console.log('\n2 -----------------');
j = `{"abc": "def"}`;
console.log( j, isJsonString(j) );
console.log('\n3 -----------------');
j = '{"abc": "def}';
console.log( j, isJsonString(j) );
console.log('\n4 -----------------');
j = '{}';
console.log( j, isJsonString(j) );
console.log('\n5 -----------------');
j = '[{}]';
console.log( j, isJsonString(j) );
console.log('\n6 -----------------');
j = '[{},]';
console.log( j, isJsonString(j) );
console.log('\n7 -----------------');
j = '[{"a":1, "b": 2}, {"c":3}]';
console.log( j, isJsonString(j) );
When you run the code above, you will get the following results:
1 -----------------
abc false
2 -----------------
{"abc": "def"} true
3 -----------------
{"abc": "def} false
4 -----------------
{} true
5 -----------------
[{}] true
6 -----------------
[{},] false
7 -----------------
[{"a":1, "b": 2}, {"c":3}] true
Please, try the snippet below and let us know if this works for you. :)
IMPORTANT: the function presented in this post was adapted from https://airbrake.io/blog/javascript-error-handling/syntaxerror-json-parse-bad-parsing where you can find more and interesting details about the JSON.parse() function.
function isJsonString( jsonString ) {
let printError = function(error, explicit) {
console.log(`[${explicit ? 'EXPLICIT' : 'INEXPLICIT'}] ${error.name}: ${error.message}`);
}
try {
JSON.parse( jsonString );
return true; // It's a valid JSON format
} catch (e) {
return false; // It's not a valid JSON format
}
}
console.log('\n1 -----------------');
let j = "abc";
console.log( j, isJsonString(j) );
console.log('\n2 -----------------');
j = `{"abc": "def"}`;
console.log( j, isJsonString(j) );
console.log('\n3 -----------------');
j = '{"abc": "def}';
console.log( j, isJsonString(j) );
console.log('\n4 -----------------');
j = '{}';
console.log( j, isJsonString(j) );
console.log('\n5 -----------------');
j = '[{}]';
console.log( j, isJsonString(j) );
console.log('\n6 -----------------');
j = '[{},]';
console.log( j, isJsonString(j) );
console.log('\n7 -----------------');
j = '[{"a":1, "b": 2}, {"c":3}]';
console.log( j, isJsonString(j) );
Try, Catch block will help you to solve this
Make a function
function IsJson(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
Example:
console.log(IsJson('abc')) // false
console.log(IsJson('[{"type":"email","detail":"john#example.com"}]')) // true
Try this
if ( typeof is_json != "function" )
function is_json( _obj )
{
var _has_keys = 0 ;
for( var _pr in _obj )
{
if ( _obj.hasOwnProperty( _pr ) && !( /^\d+$/.test( _pr ) ) )
{
_has_keys = 1 ;
break ;
}
}
return ( _has_keys && _obj.constructor == Object && _obj.constructor != Array ) ? 1 : 0 ;
}
It works for the example below
var _a = { "name" : "me",
"surname" : "I",
"nickname" : {
"first" : "wow",
"second" : "super",
"morelevel" : {
"3level1" : 1,
"3level2" : 2,
"3level3" : 3
}
}
} ;
var _b = [ "name", "surname", "nickname" ] ;
var _c = "abcdefg" ;
console.log( is_json( _a ) );
console.log( is_json( _b ) );
console.log( is_json( _c ) );
Based on #Martin Wantke answer, but with some recommended improvements/adjusts...
// NOTE: Check JavaScript type. By Questor
function getJSType(valToChk) {
function isUndefined(valToChk) { return valToChk === undefined; }
function isNull(valToChk) { return valToChk === null; }
function isArray(valToChk) { return valToChk.constructor == Array; }
function isBoolean(valToChk) { return valToChk.constructor == Boolean; }
function isFunction(valToChk) { return valToChk.constructor == Function; }
function isNumber(valToChk) { return valToChk.constructor == Number; }
function isString(valToChk) { return valToChk.constructor == String; }
function isObject(valToChk) { return valToChk.constructor == Object; }
if(isUndefined(valToChk)) { return "undefined"; }
if(isNull(valToChk)) { return "null"; }
if(isArray(valToChk)) { return "array"; }
if(isBoolean(valToChk)) { return "boolean"; }
if(isFunction(valToChk)) { return "function"; }
if(isNumber(valToChk)) { return "number"; }
if(isString(valToChk)) { return "string"; }
if(isObject(valToChk)) { return "object"; }
}
NOTE: I found this approach very didactic, so I submitted this answer.
Peter's answer with an additional check! Of course, not 100% guaranteed!
var isJson = false;
outPutValue = ""
var objectConstructor = {}.constructor;
if(jsonToCheck.constructor === objectConstructor){
outPutValue = JSON.stringify(jsonToCheck);
try{
JSON.parse(outPutValue);
isJson = true;
}catch(err){
isJson = false;
}
}
if(isJson){
alert("Is json |" + JSON.stringify(jsonToCheck) + "|");
}else{
alert("Is other!");
}
I have a pretty lazy answer to this, which will not throw an error if you try to parse a string/other values.
const checkForJson = (value) => {
if (typeof value !== "string") return false;
return value[0] === "{" && value[value.length - 1] === "}";
}
You can use this to check the value of your keys while you make some recursive func; sorry if this doesn't answer the question completely
Ofc this isn't the most elegant solution and will fail when a string actually starts with "{" and ends with "}" although those use cases would be rare, and if you really wanted, you can check for a presence of quotes or other nonsense... anyway, use at your own discretion.
TLDR: it's not bulletproof, but it's simple and works for the vast majority of use cases.
lodash is also the best bet to check these things.
function Foo() {
this.a = 1;
}
_.isPlainObject(new Foo);
// => false
_.isPlainObject([1, 2, 3]);
// => false
_.isPlainObject({ 'x': 0, 'y': 0 });
// => true
_.isPlainObject(Object.create(null));
// => true
https://www.npmjs.com/package/lodash
https://lodash.com/docs/#isPlainObject
Quickly check for a JSON structure using lodash-contrib:
const _ = require('lodash-contrib');
_.isJSON('{"car": "ferarri"}'); //true for stringified
_.isJSON({car: "ferarri"}); //true
Usage guide: this blog entry
try this dirty way
('' + obj).includes('{')

Convert Javascript Object (incl. functions) to String

Hey, Im trying to convert specific javascript objects to a String. So far I'm working with json2.js. As soon as my Object contain functions, those functions are stripped. I need a way to convert functions too, any ideas?
There is a toString() method for functions in firefox, but how to make that work with json2.js?
Actually, I think it is possible and easy. At least when doing jsonP with nodeJS it works for me just fine, and it's demonstratable by the following fiddle.
I did it by simply adding strings to a function:
var anyString = '';
var aFunction = function() { return true; };
var functionToText = anyString + aFunction;
console.log(functionToText);
here's the fiddle: http://jsfiddle.net/itsatony/VUZck/
Use String() function http://www.w3schools.com/jsref/jsref_string.asp
var f = function(a, b){
return a + b;
}
var str = String(f);
convert obj to str with below function:
function convert(obj) {
let ret = "{";
for (let k in obj) {
let v = obj[k];
if (typeof v === "function") {
v = v.toString();
} else if (v instanceof Array) {
v = JSON.stringify(v);
} else if (typeof v === "object") {
v = convert(v);
} else {
v = `"${v}"`;
}
ret += `\n ${k}: ${v},`;
}
ret += "\n}";
return ret;
}
input
const input = {
data: {
a: "#a",
b: ["a", 2]
},
rules: {
fn1: function() {
console.log(1);
}
}
}
const output = convert(input)
output
`{
data: {
a: "#a",
b: ["a", 2]
},
rules: {
fn1: function() {
console.log(1);
}
}
}`
// typeof is String
convert back
const blob = new Blob([output], { type: 'application/javascript' })
const url = URL.createObjectURL(blob)
import(url).then(input => {
/** parse input here **/
})
The short answer is that you cannot convert arbitrary JavaScript functions to strings. Period.
Some runtimes are kind enough to give you the string serialization of functions you defined but this is not required by the ECMAScript language specification. The "toString()" example you mentioned is a good example of why it cannot be done - that code is built in to the interpreter and in fact may not be implemented in JavaScript (but instead the language in which the runtime is implemented)! There are many other functions that may have the same constraints (e.g. constructors, built-ins, etc).
I made a improved version based on the #SIMDD function, to convert all types of objects to string.
Typescript code:
function anyToString(valueToConvert: unknown): string {
if (valueToConvert === undefined || valueToConvert === null) {
return valueToConvert === undefined ? "undefined" : "null";
}
if (typeof valueToConvert === "string") {
return `'${valueToConvert}'`;
}
if (
typeof valueToConvert === "number" ||
typeof valueToConvert === "boolean" ||
typeof valueToConvert === "function"
) {
return valueToConvert.toString();
}
if (valueToConvert instanceof Array) {
const stringfiedArray = valueToConvert
.map(property => anyToString(property))
.join(",");
return `[${stringfiedArray}]`;
}
if (typeof valueToConvert === "object") {
const stringfiedObject = Object.entries(valueToConvert)
.map((entry: [string, unknown]) => {
return `${entry[0]}: ${anyToString(entry[1])}`;
})
.join(",");
return `{${stringfiedObject}}`;
}
return JSON.stringify(valueToConvert);
}
Vanilla Javascript code:
function anyToString(valueToConvert) {
if (valueToConvert === undefined || valueToConvert === null) {
return valueToConvert === undefined ? "undefined" : "null";
}
if (typeof valueToConvert === "string") {
return `'${valueToConvert}'`;
}
if (typeof valueToConvert === "number" ||
typeof valueToConvert === "boolean" ||
typeof valueToConvert === "function") {
return valueToConvert.toString();
}
if (valueToConvert instanceof Array) {
const stringfiedArray = valueToConvert
.map(property => anyToString(property))
.join(",");
return `[${stringfiedArray}]`;
}
if (typeof valueToConvert === "object") {
const stringfiedObject = Object.entries(valueToConvert)
.map((entry) => {
return `${entry[0]}: ${anyToString(entry[1])}`;
})
.join(",");
return `{${stringfiedObject}}`;
}
return JSON.stringify(valueToConvert);
}
ATENTION!
I am using the function Object.entries(), winch currently is a draft. So if you are not using Babel or typescript to transpile your code, you can replace it with a for loop or the Object.keys() method.
Combining a few options
var aObj = {
v: 23,
a: function() {
return true;
}
};
var objStr = '';
for (var member in aObj) {
objStr += (objStr ? ',\n': '')+
member + ':' + aObj[member] + '';
}
console.log('{\n'+
objStr + '\n}');
JSFiddle
functionName.toString() will return a string of all the function code.
I cut is after the name.
var funcString = CurrentButton.clickFunc.toString();
console.log("calling:" + funcString.substr(0, funcString.indexOf(")")-1));
// utility for logging
var log = function(s){
var d = document.getElementById('log');
var l = document.createElement('div');
l.innerHTML = (typeof s === 'object')?JSON.stringify(s):s;
d.appendChild(l);
}
// wrapper function
var obj = {
'x-keys': {
'z': function(e){console.log(e);},
'a': [function(e){console.log('array',e);},1,2]
},
's': 'hey there',
'n': 100
};
log(obj);
// convert the object to a string
function otos(obj){
var rs = '';
var not_first = false;
for(var k in obj){
if(not_first) rs += ',';
if(typeof obj[k] === 'object'){
rs += '"'+k+'": {'+otos(obj[k])+'}';
}
else if(typeof obj[k] === 'string' || typeof obj[k] === 'function'){
rs += '"'+k+'":"'+obj[k]+'"';
}
else if(typeof obj[k] === 'number'){
rs += '"'+k+'":'+obj[k]+'';
}
else {
// if it gets here then we need to add another else if to handle it
console.log(typeof obj[k]);
}
not_first = true;
}
return rs;
}
// convert a string to object
function stoo(str){
// we doing this recursively so after the first one it will be an object
try{
var p_str = JSON.parse('{'+str+'}');
}catch(e){ var p_str = str;}
var obj = {};
for(var i in p_str){
if(typeof p_str[i] === 'string'){
if(p_str[i].substring(0,8) === 'function'){
eval('obj[i] = ' + p_str[i] );
}
else {
obj[i] = p_str[i];
}
}
else if(typeof p_str[i] === 'object'){
obj[i] = stoo(p_str[i]);
}
}
return obj;
}
// convert object to string
var s = otos(obj);
log(s);
// convert string to object
var original_obj = stoo(s);
log(original_obj);
log( original_obj['x-keys'].z('hey') );
log( original_obj['x-keys'].a[0]('hey') );
<div id='log'></div>
I realize this is very old but I have a solution here
https://jsfiddle.net/stevenkaspar/qoghsxhd/2/
May not work for all cases but it is a good start
It will convert this into a string and then back into an object and you can run the functions
var obj = {
'x-keys': {
'z': function(e){console.log(e);},
'a': [function(e){console.log('array',e);},1,2]
},
's': 'hey there',
'n': 100
};
Just provide the object to this function. (Look for nested function) Here:
function reviveJS(obj) {
return JSON.parse(JSON.stringify(obj, function (k, v) {
if (typeof v === 'function') {
return '__fn__' + v;
}
return v;
}), function (k, v) {
if (typeof v === 'string' && v.indexOf('__fn__') !== -1) {
return v;
}
return v;
});
}
UPDATE
A slightly decent code than above which I was able to solve is here http://jsfiddle.net/shobhit_sharma/edxwf0at/
I took one of answers above, it worked fine, but didn't inlcude case then array includes function. So i modified it and it works fine for me.. Sharing the code.
function convert(obj,ret="{") {
function check(v) {
if(typeof v === "function") v = v.toString()
else if (typeof v === "object") v = convert(v)
else if (typeof v == "boolean" || Number.isInteger(v)) v=v
else v = `"${v}"`
return v
}
if(obj instanceof Array) {
ret="["
obj.forEach(v => {
ret += check(v)+','
});
ret += "\n]"
} else {
for (let k in obj) {
let v = obj[k];
ret += `\n ${k}: ${check(v)},`;
}
ret += "\n}";
}
return ret
}
So I was just testing your script on one of my project, and there's a problem with Object keys that contain special characters (like / or -).
You should consider wrapping theses keys with quotes.
return `"${entry[0]}" : ${anyToString(entry[1])}`;

Categories

Resources