Suppose that I have this code:
var cfg = {
config: {
fields: {
name: {
type: 'text'
},
age: {
type: 'number'
}
},
actions: [
{
label: 'Print',
cb: (model) => {
console.log(model);
}
}
]
},
data: {name: 'Jhon', age: 23}
}
And I want to convert it to a string (to let a user edit it) and then convert it back to executable code, any idea in how to achieve this?
I tried with JSON.stringify and JSON.parse but that will of course strip the functions. .toString returns "[object Object]", iterating on the object and call .toString when the values is a string, function or number is a possibility, any other idea?
The Function constructor takes code as string, so does eval and a couple other. However, if in any way avoidable, do not convert code to string and backwards because of security concerns, ability to debug and a lot of other issues you can run into when doing so.
Converting code to a string is slightly annoying, because you need to make sure you don't redeclare variables and everything in the new context is syntactically correct, e.g. note that obj's f property is again named in the declaration, because it is later given to eval which places it in the global scope where it needs a name.
let obj = { f: function f() { let stuff = "hi"; console.log("hi"); } };
let code = obj.f.toString();
console.log(code);
eval(code);
f();
Note that JSON.stringify has an optional replacer parameter which can be used to customize the process.
I will again highly advise to avoid any code to/from string conversions when possible, in all normal cases this is not needed and should not be done.
You can iterate around that object and set it to inputs elements, like this
for (var key in cfg.config.fields) {
console.log(key, cfg.config.fields[key].type);
console.log(key, cfg.data[key],'data');
console.log('<input type="'+cfg.config.fields[key].type+'" value="'+cfg.data[key]+'">')
}
This is the solution I came up with:
const toString = (code) => {
if(Array.isArray(code)){
return code.map(item => toString(item));
}
else if(typeof code == 'object'){
let tmp = {};
Object.keys(code).forEach(key => {
tmp[key] = toString(code[key])
});
return tmp;
}
else{
return code.toString().split('\n').join('').replace(/ +(?= )/gmi, '');
}
};
This will iterate recursively across all the properties in a random JS structure (an object containing objects and arrays) an return the corresponding structure containing strings, you can then use JSON.stringify to get the actual string.
After the user edited it, it is executed with:
eval(`(${string})`);
As noted by other, be careful using eval, it can be dangerous (an interesting article is https://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/)
Related
i am not getting proper the return after insertgraph in objection.js
i am getting the result like :
[
User {
name: 'Santosh Devi',
city: 'Suratgarh',
number: '9898987458',
userroles: UserRoles { role_id: 2, user_id: 37 },
id: 37
}
]
where i want the result like :
[
{
name: 'Santosh Devi',
city: 'Suratgarh',
number: '9898987458',
userroles: { role_id: 2, user_id: 37 },
id: 37
}
]
There are few ways to get rid of the specific class references:
1. JSON.parse(JSON.stringify(result))
This will rebuild the object by first converting the whole object to a string (in JSON format), and then by doing the reverse -- creating a new object from a string. As this string format (JSON) does not store custom class information, it achieves your purpose. However, if your object has functions, symbols, then these will be omitted. Also Map and Set will become empty objects. For a more complete list of restrictions. See JSON.stringify
2. Deep Clone
There are several deep-clone functions out there, that may or may not do what you expect. Some will still try to maintain the original prototype references, so that it would not benefit you. You can find some here: How to Deep clone in javascript. For your case, this one would do the job:
function deepClone(obj, hash = new WeakMap()) {
if (Object(obj) !== obj) return obj; // primitives
if (hash.has(obj)) return hash.get(obj); // cyclic reference
const result = Array.isArray(obj) ? [] : {};
hash.set(obj, result);
return Object.assign(result, ...Object.keys(obj).map(
key => ({ [key]: deepClone(obj[key], hash) }) ));
}
You call it as newResult = deepClone(result).
The advantage here, is that it supports cyclic references, which JSON.stringify cannot handle. Also, there is no string conversion happening, which really is not necessary. You can extend this function to keep deal with some class instances that you like to stay that way. See how you can support Date, RegExp, Map, Set, ... in this answer. But don't do the "catch-all" line.
3. Change the prototype
With this strategy you mutate the result in-place.
function removeClasses(obj, hash = new WeakSet()) {
if (Object(obj) !== obj) return; // primitives
if (hash.has(obj)) return; // cyclic reference
hash.add(obj);
if (Array.isArray(obj)) Object.setPrototypeOf(obj, Array.prototype);
else Object.setPrototypeOf(obj, Object.prototype);
for (let value of Object.values(obj)) {
removeClasses(value, hash);
}
}
Call it as removeClasses(result), and afterwards result will have been "fixed". Again, this method does not use a conversion to string. As it does not create a new object either, it consumes less memory. But on the other hand you mutate an object, and some would advise against that.
If I create an object and use console.log it will expand the object and I can see the object properties. If I use alert it does not. Is there any way to do the same thing in an alert?
car=new Object();
car.wheels=4;
car.make="honda";
car.owner="me"
console.log(car);
output: Object { wheels: 4, make: "honda", owner: "me" }
alert(car)
output is [object Object]
How can I get the same output for the alert?
As others have stated you use JSON.stringify.
But I wanted to give a few other pointers. I don't know if you know these already, but your example indicated that you might appreciate the tips.
In JavaScript it is recommended to not use new Object() Instead just create your object:
var car = {
wheels: 4,
make: "honda",
owner: "me"
}
Always use var, let or const when creating variables. If you don't then they are created on the global scope. In the browser you would be creating the new variables on the window object.
JSON.stringify has other parameters:
JSON.stringify(value[, replacer[, space]])
The replacer is not often used, but it can provide a way of filtering and preventing recursive data.
The space parameter can be a number form 0 to 10 or a number of characters in a string from 0 to 10. This indicates how much to indent each level in the output.
function replacer(key, value) {
if (typeof value === 'string') {
return undefined;
}
return value;
}
var foo = {
company: 'Sony',
model: 'Playstation',
version: 4,
pricing: [
{
pro: false,
price: 299.00
},
{
pro: true,
price: 399.00
}
],
comments: 'none'
};
alert(JSON.stringify(foo, replacer, 2));
alert(JSON.stringify(foo, replacer, '----'));
In the console log method, the parameter is considered as an object. So, the object can be in any form like an array, string, integer, etc., and We will get the content. But in the alert method, it accepts only the string. So, if you send the object, it will convert it as string version of the object ( object Object ). If you stringify the object and send as a parameter to the alert method, it will display the content. Try this one,
window.alert(JSON.stringify(object));
You can use this alert(JSON.stringify(object))
I'm attempting to write a function that takes an array of Strings as its input. The function should return an object with (1) key, value pair. The first element of the array should be the property name and the last element of the array should be its key.
function transformFirstAndLast(array){
return {`${array[0]}`: array[length-1];}
}
The above gives me an error. Can someone provide a detailed explanation why this isn't working? I'd like to avoid creating separate variables to store the values from the first and last array indices.
Your question really boils down to, "Can I use expressions as keys in object literals?"
The answer is yes (since es6):
function yell(template, ...parts) {
return parts[0] + '!';
}
function foo() {
return 'bar';
}
class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
toString() {
return `${this.first} ${this.last}`;
}
}
let names = ['Abe'];
let my_obj = {
[3+5]: 'some_value',
[yell `${foo()}`]: foo(),
[names[0]]: 64,
[new Person('Rafael', 'Cepeda')]: 25
};
console.log(my_obj);
As long as the expression evaluates to a string, all is fair.
You are missing the { and you can't use a template strings as a key. To use a "variable" as a key in an object, you should use the brakets around the variable. Here's the working code.
function transformFirstAndLast(array){
return {[array[0]]: array[array.length-1]};
}
I guess template literal is unnecessary here simply try like this way after fixing this line array[length-1] because its wrong, correct one is array[array.length-1]. I've added more verbose code but you can also do the shorthand version like return {[array[0]]: array[array.length-1]}; on your transformFirstAndLast(array) function.
function transformFirstAndLast(array){
const result = {};
result[array[0]] = array[array.length-1];
return result;
}
console.log(transformFirstAndLast(['a','b','c','d','e']))
yes you can use string literals as key. But the thing is that they are calculated in the run time. So you need to treat them as expressions. And to have variables/expressions as your keys you need to wrap them inside []
let a = {
`key`: value
} // is not allowed
let a = {
[`key`]: value
} // is allowed since you have wrapp
for your case
return {[`${array[0]}`]: array[array.length-1]};
Now since you have wrapped the array[0] item inside a string literal, you will get string values for your zero'th item. If your array[0] is to be a object this would not work as well. It needs to be either string or number. Or else you would get "[object Object]" as your key
var input = ["name", "fname", "lname", "stackOverflow"];
function transformFirstAndLast(array){
return {[array[0]]: array.pop()}
}
responseObject = transformFirstAndLast(input)
console.log(responseObject)
I'm trying to implement a variation of a trie in JavaScript. Basically, it's an efficient data storage object in which the characters in keys are not repeated. In other words, if I have the keys "abe" and "ann," only one instance of the shared letter "a" should appear:
{
a: {
b: {
e: {
0: 'lincoln'
}
},
n: {
n: {
0: 'mcgee'
}
}
}
}
Here is the desired implementation and a few usage examples:
function Trie () {
// The top level of the trie.
var root = {};
return {
write: function (key, value) {
},
read: function (key) {
}
};
}
// Sample usage
var trie = new Trie();
trie.write('abe', 'lincoln');
trie.write('ann', 'mcgee');
trie.read('abe'); // returns 'lincoln'
trie.read('ann'); // returns 'mcgee'
I've run into a blocker with respect to the write method. Given a string key such as "abe," I need to assign a property to root['a']['b']['e']. I can't find a way to assign a value to an object property several layers deep when the number of keys and the values of the keys are unknown.
The only solution that comes to mind is, I think, a bad one: placing the path to the value into a string and using eval. For example: eval("root['a']['b']['e'] = 'lincoln'");
Is there a better solution for dynamically assigning the values? (I realize that this is a bit of complicated problem, so I'm happy to clarify by providing extra information.)
a very naive approach (given the requirements,though i would write a different implementation)
given a string of keys and a pointer to the root,and a value to assign;
function write(root,path,value){
var a = path.split(''); // 'abc'->['a','b','c']
var pointer = root;
var i=0;
while(i<a.length-1){
if(pointer[a[i]] == undefined){
pointer[a[i]]={};
}
pointer = pointer[a[i]];
i++;
}
pointer[a[i]]=value;
return root;
}
EDIT : i'm assuming all the keys exist on their respective object. I added a if condition in case some keys are not defined.
EDIT:2 split corrected, correcting a little bug right now ;)
EDIT:3 should work now.
usage : write({},'abc',1) // yields {a:{b:{c:1}}}
what you're looking for is a double array trie.
you can do a github search for that, but the two main libraries listed are:
doublearray, from the documentation:
var doublearray = require('./doublearray.js');
var words = [
{ k: 'a', v: 1 },
{ k: 'abc', v: 2 },
];
var trie = doublearray.builder().build(words);
trie.contain('a'); // -> true
trie.lookup('abc'); // -> 2
or datrie
http.get(options, function(res){
fs.appendFile('log.txt', JSON.stringify(res.headers, null, 4));
})
I have a question regarding the JSON.stringify() function.
I've learned that simply using the res.headers does not in fact output to JSON format.
At the moment, I am restricted to only being able to use one res.xxxxx method within JSON.stringify(). The piece of code in question is pasted above. How am I able to use more than one value? At the moment, I can only put in res.headers into the value parameter. I would also like to use res.statusCode and my own objects, all stringified under one brace {}.
The parameters of JSON.Stringify is as follows: JSON.stringify(value, [replacer], [space]);
You need to create a new js object and put res.headers into it.
var obj = {};
obj.headers = res.headers;
obj.somethingelse = somethingelse;
var string = JSON.stringify(obj);
JSON is always a single value. So the output out JSON.stringify can always only be a single value. It would make sense to have the input be a single value too. This is like asking why can't my function return two things? You can make it return some composite value, but that means you're still returning a single (composite) value. The solution here is the same, compose your input.
var reply = {
code: res.statusCode,
headers: parse_http_headers(res.headers),
etc: /* etc */
};
log(JSON.stringify(reply));
Note that you must write parse_http_headers yourself.
You could always add the extra things you want to the headers object...
res.headers.statusCode = res.statusCode
JSON.stringify(res.headers, null, 4);
I don't know if there are any bad side effects if you mutate the res object in node. You might want to consider creating a shallow copy of the headers object if you are worried about that.
You could as well stringify more than only the headers part of your object:
JSON.stringify(res, …)
If you want to stringify only certain parts of your object, you can
filter them with the replacer function,
delete everything else before,
or build a new object to be stringified:
JSON.stringify({
heads: res.headers,
…
}, …)
If you'd like to flatten several objects, you can use this function.
function merge() {
var out = {};
insert = function(e) {
for (var i in e) {
if (Object.prototype.hasOwnProperty.call(e, i))
out[i] = e[i];
}
};
for (var i = 0; i < arguments.length; i++) {
insert(arguments[i]);
}
return out;
}
var a = {'a': 'aa'};
var b = {'b': 'bb', 'bbb': 'bbbb'};
var c = {'c': 'cc'};
var combined = merge(a,b,c);