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; })
Related
Experimenting with an idea. Given an object like:
T = {
a: 2,
b: 9,
c: {
a: 3,
d: 6,
e: {
f: 12
}
}
}
I want to mutate it such that every value that is an object, changes to the same object, with the parent object as prototype.
Meaning I'd like to be able to have the following outputs:
> T.c.b
9
> T.c.e.b
9
> T.c.e.a
3
> T.c.c.c
{a: 3, d: 6, e:[Object]}
I have already created the following functions that work almost as expected:
function chainer(object) {
for (const key in object) {
if (object[key] !== null && typeof (object[key]) === 'object') {
let Constructor = function () {
};
Constructor.prototype = object;
let objectValue = {...object[key]};
object[key] = new Constructor();
for (const savedKey in objectValue) {
object[key][savedKey] = objectValue[savedKey];
}
}
}
}
function chain(object) {
chainer(object);
for (const key in object) {
if (object[key] !== null && typeof (object[key]) === 'object') {
chainer(object[key]);
}
}
}
With the previous example it works as expected. Nevertheless, when I try with the following:
T = {a:4, g:{g:{g:{g:{g:{g:{g:{}}}}}}}}
The following output happens:
> T.a
4
> T.g.a
4
> T.g.g.a
4
> T.g.g.g.a
undefined
> T.g.g.g.g.a
undefined
I find it weird it only works up to a point, it makes me think perhaps its an issue with some limit I'm not aware.
Anyway, I'm getting dizzy and out of ideas, any thoughts?
This seems to work fine:
ouroboros = (x, parent = null) => {
if (!x || typeof x !== 'object')
return x;
let r = Object.create(parent);
Object.entries(x).forEach(([k, v]) => r[k] = ouroboros(v, r));
return r;
};
//
T = ouroboros({x: 4, a: {b: {c: {d: {e: {}}}}}});
console.log(T.a.b.c.a.b.c.a.b.c.a.b.c.a.b.c.x);
or, mutating objects, instead of copying:
ouroboros = (x, parent = null) => {
if (x && typeof x === 'object') {
Object.setPrototypeOf(x, parent);
Object.values(x).forEach(v => ouroboros(v, x));
}
};
If I am not mistaking you wanted to do something like this:
rec = function (o) {
return Object.keys(o).reduce((acc, key) => {
if (typeof acc[key] === "object") {
const kv = {...rec(acc[key]), ...o}
return {...acc, ...kv, get [key]() { return this}}
}
return acc;
},o)
}
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)
};
I want to implement a function that returns an array of property values if the value is primitive (non-object or array) and property name starts with prefix.
For example
var values = function (obj, prefix) { ... }
var testObj = {
'a': 1,
'ab': [
{
'c': 2,
'ac': true
}
]
};
As a result of values(testObj, 'a') function invocation I expect to get such array of primitives: [1, true].
Here is my try:
var values = function (obj, prefix) {
var res = [];
for (var i in obj) {
if (i.startsWith(prefix)) {
var v = obj[i];
if (typeof v === 'object') {
var r0 = arguments.callee(v, prefix);
res.push(r0);
} else {
res.push(v);
}
}
}
return res;
};
But it returns a wrong result: [1, []]. How can I fix it?
You could use a recursive approach for the values, you need.
function values(obj, prefix) {
var result = [];
Object.keys(obj).forEach(function (k) {
if (obj[k] !== null && typeof obj[k] === 'object') {
result = result.concat(values(obj[k], prefix));
} else if (k.startsWith(prefix)) {
result.push(obj[k]);
}
});
return result;
}
var testObj = { 'a': 1, 'ab': [{ 'c': 2, 'ac': true }] },
result = values(testObj, 'a');
console.log(result);
Following code works.
var testObj = {
'a': 1,
'ab': [
{
'c': 2,
'ac': true
}
]
};
var values = function (obj, prefix) {
var res = [];
if(Array.isArray(obj)){
for(var j in obj){
res = res.concat(arguments.callee(obj[j], prefix));
}
}
else if(typeof obj == "object") {
for (var i in obj) {
if (i.startsWith(prefix)) {
var v = obj[i];
if (typeof v === 'object') {
res = res.concat(arguments.callee(v, prefix));
} else {
res.push(v);
}
}
}
}
return res;
};
console.log(values(testObj, 'a'));
This might be what you are looking for;
var testObj = {
'a': 1,
'ab': [
{
'c': 2,
'ac': true
}
]
};
getValues = (o,x) => Object.keys(o)
.reduce((p,k) => p.concat(typeof o[k] === "object" ? getValues(o[k],x)
: k.indexOf(x) >= 0 ? o[k]
: [])
,[]);
console.log(getValues(testObj,"a"));
(My city energy is weak.)
Where the main problem is in the i.startsWith(prefix) condition. It avoids you to enter a object without property name including #prefix inside a array. For example:
{ a: 1, ab: [ /* 0: { 'c': 2, 'ac': true } */ ] }
As you see, the object in the array is ignored since its property name is 0, that's its index.
If you really want to get this result: [1, true] you'll have to skip the array and return the first item to res.push.
var values = function (obj, prefix) {
var res = [];
for (var i in obj) {
if (i.startsWith(prefix)) {
var v = obj[i];
if (typeof v === 'object') {
var isArray = v instanceof Array;
var r0 = arguments.callee(isArray ? v[0] : v, prefix);
res.push(isArray && r0.length === 1 ? r0[0] : r);
} else {
res.push(v);
}
}
}
return res;
};
var testObj = {
'a': 1,
'ab': [
{
'c': 2,
'ac': true
}
]
};
var res = [];
var values = function (obj, prefix) {
for (var i in obj) {
var v = obj[i];
//Prefix check line can be moved here if you want to check the prefix for object
if (typeof v === 'object') {
arguments.callee(v, prefix);
} else {
if (i.startsWith(prefix)) { //Prefix Check
res.push(v);
}
}
}
return res;
};
console.log(values(testObj,'a'));
Please check this, this gives the output that you wanted.
Is there a way I can do a shallow comparison that will not go down and compare the contents of objects inside of objects in Javascript or lodash? Note that I did check lodash, but it appears to perform a deep comparison which I don't want to do.
var a = { x: 1, y: 2}
var b = { x: 1, y: 3}
Is there some way for example to compare a and b?
Simple ES6 approach:
const shallowCompare = (obj1, obj2) =>
Object.keys(obj1).length === Object.keys(obj2).length &&
Object.keys(obj1).every(key => obj1[key] === obj2[key]);
Here I added the object keys amount equality checking for the following comparison should fail (an important case that usually does not taken into the account):
shallowCompare({ x: 1, y: 3}, { x: 1, y: 3, a: 1}); // false
2019 Update. Per Andrew Rasmussen' comment we also need to take into account undefined case. The problem with the previous approach is that the following comparison returns true:
({ foo: undefined })['foo'] === ({ bar: undefined })['foo'] // true
So, explicit keys existence check is needed. And it could be done with hasOwnProperty:
const shallowCompare = (obj1, obj2) =>
Object.keys(obj1).length === Object.keys(obj2).length &&
Object.keys(obj1).every(key =>
obj2.hasOwnProperty(key) && obj1[key] === obj2[key]
);
function areEqualShallow(a, b) {
for(var key in a) {
if(!(key in b) || a[key] !== b[key]) {
return false;
}
}
for(var key in b) {
if(!(key in a) || a[key] !== b[key]) {
return false;
}
}
return true;
}
Notes:
Since this is shallow, areEqualShallow({a:{}}, {a:{}}) is false.
areEqualShallow({a:undefined}, {}) is false.
This includes any properties from the prototype.
This uses === comparison. I assume that is what you want. NaN === NaN is one case that may yield unexpected results. If === is not what you want, substitute with the comparison you want.
EDIT: If the same keys are in each object, then
function areEqualShallow(a, b) {
for(var key in a) {
if(a[key] !== b[key]) {
return false;
}
}
return true;
}
Paul Draper's solution can be optimized by removing the compare in the second pass.
function areEqualShallow(a, b) {
for (let key in a) {
if (!(key in b) || a[key] !== b[key]) {
return false;
}
}
for (let key in b) {
if (!(key in a)) {
return false;
}
}
return true;
}
keeping in mind that it only for shallow and only for strings and numbers
function equals(obj1, obj2) {
return Object.keys(obj1)
.concat(Object.keys(obj2))
.every(key => {
return obj1[key] === obj2[key];
});
}
This is lifted from fbjs:
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* #typechecks
*
*/
/*eslint-disable no-self-compare */
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x, y) {
// SameValue algorithm
if (x === y) {
// Steps 1-5, 7-10
// Steps 6.b-6.e: +0 != -0
return x !== 0 || 1 / x === 1 / y;
} else {
// Step 6.a: NaN == NaN
return x !== x && y !== y;
}
}
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA, objB) {
if (is(objA, objB)) {
return true;
}
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
return false;
}
var keysA = Object.keys(objA);
var keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (var i = 0; i < keysA.length; i++) {
if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
module.exports = shallowEqual;
I recommend copying it into your own project if you need to use it, as their README clearly states that they may remove or modify this and any other code in the lib without warning.
To do a "shallow" comparison where inherited properties should be ignored and NaN should equal NaN, the following should do the job. It checks that each object has the same own properties and that the values are === or both NaN:
function checkProperties(a, b) {
var equal = true;
// For each property of a
for (var p in a) {
// Check that it's an own property
if (a.hasOwnProperty(p)) {
// Check that b has a same named own property and that the values
// are === or both are NaN
if (!b.hasOwnProperty(p) ||
(b[p] !== a[p] && !(typeof b[p] == 'number' && typeof a[p] == 'number' && isNaN(b[p] && isNaN(a[p]))))) {
// If not, set equal to false
equal = false;
}
}
// If equal is false, stop processing properties
if (!equal) break;
}
return equal;
}
Using recent features like Object.keys to get own properties, then
function checkProperties(a, b) {
return Object.keys(a).every(function(p) {
return b.hasOwnProperty(p) &&
(b[p] == a[p] || (typeof a[p] == 'number' && typeof b[p] == 'number' && isNaN(b[p]) && isNaN(a[p])));
});
}
// Compare a to b and b to a
function areEqualShallow(a, b) {
return checkProperties(a, b) && checkProperties(b, a);
}
// Minimal testing
var a = {foo:'a', bar:2};
var b = {foo:'a', bar:2};
var c = {foo:'c', bar:2};
var d = {foo:'a', bar:2, fum:0};
console.log('a equal to b? ' + areEqualShallow(a,b)); // true
console.log('a equal to c? ' + areEqualShallow(a,c)); // false
console.log('a equal to d? ' + areEqualShallow(a,d)); // false
With newer features, the checkProperties function can be simplified somewhat:
const shallowEq = (a, b) =>
[...Object.keys(a), ...Object.keys(b)].every((k) => b[k] === a[k]);
If you really need to check undefined values, then this extension should satisfy #AndrewRasmussen:
const shallowEq2 = (a, b) =>
[...Object.keys(a), ...Object.keys(b)].every(k => b[k] === a[k] && a.hasOwnProperty(k) && b.hasOwnProperty(k));
In most use cases you don't really need all the checks, and you only want to see if b contains everything a contains. Then an a-centric check would be really really terse:
const shallowEq3 = (a, b) => Object.keys(a).every(k => b[k] === a[k]);
var a = { x: 1, y: 2}
var b = { x: 1, y: 3}
function shalComp (obj1, obj2) {
var verdict = true;
for (var key in obj1) {
if (obj2[key] != obj1[key]) {
verdict = false;
}
}
return verdict;
}
const isEqual = (a, b) => {
// compare keys
const xKeys = Object.keys(a);
const bKeys = Object.keys(b);
if (xKeys.length !== bKeys.length) {
return false;
}
// compare values
for (let objKeys in xKeys) {
if (xKeys[objKeys !== bKeys[objKeys]]) {
return false;
}
}
return true;
};
var a = {
x: 1,
y: 2,
};
var b = {
x: 1,
y: 2,
};
console.log(isEqual(a, b)); // true
And you can see this video is very helpful for your question : JS Tutorial: Find if Two Object Values are Equal to Each Other
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])}`;