Convert user input string to an object to be accessed by function - javascript

I have data in the format (input):
doSomething({
type: 'type',
Unit: 'unit',
attributes: [
{
attribute: 'attribute',
value: form.first_name
},
{
attribute: 'attribute2',
value: form.family_name
}
],
groups: [
{
smth: 'string1',
smth2: 'string2',
start: timeStart.substring(0, 9)
}
]
})
I managed to take out the doSomething part with the parenthesis as to load the function from the corresponding module with
expression.split('({',1)[0]
However using the loaded function with the rest, obtained with:
expression.split(temp+'(')[1].trim().replace(/\n+/g, '').slice(0, -1)
does not work because it should be an object and not a string. Hardcoding the data in does work as it is automatically read as an object.
My question is if there is any way of converting the string that I get from the user and convert it to an object. I have tried to convert it to a json object with JSON.parse but I get an unexpected character t at position 3. Also I have tried new Object(myString) but that did not work either.
What I would like is to have the body of the provided function as an object as if I would hard code it, so that the function can evaluate the different fields properly.
Is there any way to easily achieve that?
EDIT: the "output" would be:
{
type: 'type',
Unit: 'unit',
attributes: [
{
attribute: 'attribute',
value: form.first_name
},
{
attribute: 'attribute2',
value: form.family_name
}
],
groups: [
{
smth: 'string1',
smth2: 'string2',
start: timeStart.substring(0, 9)
}
]
}
as an object. This is the critical part because I have this already but as a string. However the function that uses this, is expecting an object. Like previously mentioned, hard coding this would work, as it is read as an object, but I am getting the input mentioned above as a string from the user.

Aside: I know eval is evil. The user could do by this certain injections. This is only one possibility to do this there are certain other ways.
I just added before "output =", cut from the input-string the "doSomething(" and the last ")". By this I have a normal command-line which I could execute by eval.
I highly not recommend to use eval this way; especially you don't
know what the user will do, so you don't know what could all happen
with your code and data.
let form = {first_name: 'Mickey', family_name: 'Mouse'};
let timeStart = (new Date()).toString();
let input = `doSomething({
type: 'type',
Unit: 'unit',
attributes: [
{
attribute: 'attribute',
value: form.first_name
},
{
attribute: 'attribute2',
value: form.family_name
}
],
groups: [
{
smth: 'string1',
smth2: 'string2',
start: timeStart.substring(0, 9)
}
]
})`;
let pos= "doSomething(".length;
input = 'output = ' + input.substr(pos, input.length-pos-1);
eval(input);
console.log(output);

Related

Best way to properly store data in a JSON?

I'm studying about JSON and its use cases. Suppose I have a recipe book and I have the following JSON to store recipes (I apologize if anything is wrong before hand, I'm just starting with this)
var recipeBook =
{
recipes:
[
{
name: 'Spaghetti',
ingredients:
[
{
ingredientName: 'Pasta',
requiredAmount: 1,
},
{
ingredientName: 'Tomato Sauce',
requiredAmount: 1,
}
]
},
{
name: 'Cereal',
ingredients:
[
{
ingredientName = 'Cereal Box',
requiredAmount = 1
},
{
ingredientName = 'Milk',
requiredAmount = '1'
}
]
}
]
}
Say I wanted to add a third recipe, or add a new ingredient to a recipe...I'm wondering what is the best option (code-wise) to add new data into this JSON.
I think propably the function you are looking for is https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push, for example
recipeBook.recipes.push({
name: 'Soup',
ingredients:
[
{
ingredientNam: 'Water',
requiredAmount: 1
},
{
ingredientName: 'Potatoes',
requiredAmount: 4
}
]
})
And for new ingredients it would be
recipeBook.recipes[0].ingredients.push(
{
ingredientName: 'Salt',
requiredAmount: 1
}
)
I would recomend you to use the name of the recipe as the key in the recipes, I mean:
var recipeBook =
{
recipes:
{
Spaghetti:
{
ingredients:
[
{
ingredientName: 'Pasta',
requiredAmount: 1,
},
{
ingredientName: 'Tomato Sauce',
requiredAmount: 1,
}
]
},
Cereal:
{
ingredients:
[
{
ingredientName: 'Cereal Box',
requiredAmount: 1
},
{
ingredientName: 'Milk',
requiredAmount: 1
}
]
}
}
}
This helps to get the recipe as
var spaghettiRecipe = recipeBook.recipes.Spaghetti
First, JSON vs. JS Objects
JSON isn't to be confused with generic objects in JavaScript. JSON is a "lightweight data-interchange format" that does its best to be both easy for humans to read and edit, yet also easy for computers to parse and generate. One of the main differences is that JSON has stricter syntax requirements than generic JS objects do.
What you've written is simply an object declaration in Javascript, rather than standard JSON. A JSON equivalent to your example data would look like this:
{
"recipes": [
{
"name": "Spaghetti",
"ingredients": [
{
"ingredientName": "Pasta",
"requiredAmount": 1
},
{
"ingredientName": "Tomato Sauce",
"requiredAmount": 1
}
]
},
{
"name": "Cereal",
"ingredients": [
{
"ingredientName": "Cereal Box",
"requiredAmount": 1
},
{
"ingredientName": "Milk",
"requiredAmount": 1
}
]
}
]
}
The specific distinctions here:
All property names are enclosed in double quotes "
All strings are enclosed in double quotes " rather than single '
Trailing commas that aren't followed by another key/ value pair or data structure aren't allowed
You could choose to omit the recipes property and keep just the array without the enclosing object, this would still be valid.
Also worth noting that prop = value is not allowed in either syntax, JS object declaration nor JSON.
A description of the full JSON spec is here if you're interested.
Manipulating JS Data
You asked what the best option code-wise is to add data to this JSON. Since JSON is just a format, it's not actually super relevant here, since it's primarily used for data storage and transport, and not for manipulation.
To work with data stored as JSON in Javascript, you can parse it in with the JSON.parse(string) method, which will read a JSON string and return an equivalent Javascipt object. You can then use standard object/ array manipulation methods and techniques on that resulting object, in this case using push() to add a new recipe onto the array.
Here is some sample code to add data to the object
//add recipe
recipeBook.recipes.push(newRecipe);
//add ingredient to recipe by name
var recipe = recipeBook.recipes.find(r => r.name == recipeName);
if (recipe) recipe.ingredients.add(newIngredient);
Also your Cereal object is using ingredientName = 'Cereal Box', which is invalid javascript syntax. You need to use : as in your spaghetti object.
Instantiating an object involves creating an object programmatically. I'm simplifying the code to make it easy:
var book = {}
function Recipe(name, ingredients) {
this.name = name;
this.ingredients = ingredients;
}
// instantiate it
var chicken_teriyaki = new Recipe('Teriyaki', ['new one', 'two', 'three'])
var fried_chicken = new Recipe('Fried Chicken', ['chicken', 'potato starch', 'oil'])
// add to book
book.chicken_teriyaki = chicken_teriyaki;
book.fried_chicken = fried_chicken;
// update recipe
book.chicken_teriyaki.ingredients.push('another yummy')
// view result
console.log('the book', book);

How to change the location of an object key value pair in JavaScript

I've seen similar questions to this one but in different languages and I am struggling to create a JavaScript equivalent.
I am receiving an object and through a function I want to change the location of one (or more) of the properties. For example,
With the original object of
{
individual: [
{
dob: '2017-01-01',
isAuthorized: true,
},
],
business: [
{
taxId: '123',
},
],
product: {
code: '123',
},
}
I would like to change the location of isAuthorized to be in the first object inside of the business array instead of individual.
Like so
{
individual: [
{
dob: '2017-01-01',
},
],
business: [
{
taxId: '123',
isAuthorized: true,
},
],
product: {
code: '123',
},
}
So far I was trying to create an object that would contain the key name and location to change it to, e.g.
{
isAuthorized: obj.business[0]
}
And then loop over the original object as well as the object with the location values and then set the location of that key value pair.
Basically, in this function I want to see that if the original object contains a certain value (in this case isAuthorized) that it will take that key value pair and move it to the desired location.
What you want can easily be achieved by using loadsh, here's a working snippet of how to restructure based on defined structure map. Extended this example to match what you want.
The example is doing a deep clone, if you are fine modifying the original object then skip that step to avoid the overhead.
// input data
const data = {
individual: [
{
dob: '2017-01-01',
isAuthorized: true,
},
],
business: [
{
taxId: '123',
},
],
product: {
code: '123',
},
};
// the structure change map
const keyMap = {
'individual[0].isAuthorized': 'business[0].isAuthorized'
};
function parseData(data,keyMap) {
const newData = _.cloneDeep(data);
for( let [source,dest] of Object.entries(keyMap) ) {
_.set(newData,dest,_.get(newData,source));
_.unset(newData,source);
}
return newData;
}
console.log(parseData(data, keyMap));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
Note: loadsh's set consider any numeric value as an array index so if you are using a numeric object key then use loadash.setWith. I recommend reading examples in doc for a better understanding.
https://lodash.com/docs/4.17.15#set

How to convert stringified surveyjs JSON-like object into actual JSON

I'm working on a an application that uses the survey building library surveyjs. I'm building a tool where users can input a survey JSON from that website into a form that is then sent as a string via an ajax request that triggers an AWS Lambda function. The lambda function takes the ajax request and inserts their survey into a MongoDB instance using mongoose.
When the string comes into the lambda function, it looks like this:
"{ pages: [ { name: 'page1', elements: [ { type: 'radiogroup', name: 'question1', title: 'IS THIS A SURVEY?', choices: [ { value: 'item1', text: 'Yes' }, { value: 'item2', text: 'No' } ] } ] } ]}"
And when I try to parse that string, I get this error:
Error: JSON Parse error: Expected '}'
I think it might have something to do with the JSON keys not being strings. I've also read that my use of single quotes could potentially be the problem, but I've exhausted my knowledge base.
Overall, my question is: How can I convert that string into a JSON object?
Thanks!
JSON strings need their string properties and values to be double-quoted. Use a regular expression and replace:
const originalStr = "{ pages: [ { name: 'page1', elements: [ { type: 'radiogroup', name: 'question1', title: 'IS THIS A SURVEY?', choices: [ { value: 'item1', text: 'Yes' }, { value: 'item2', text: 'No' } ] } ] } ]}";
const finalStr = originalStr
.replace(/'/g, '"')
.replace(/(\w+):/g, '"$1":');
console.log(JSON.parse(finalStr).pages);
That said, it would be better to fix whatever's serving the results in the first place, if at all possible.
If your lambda function is written using javascript then you can make use of eval to parse malformed JSON, however the eval'd string is evaluated as actual javascript within the current context so to get the result you have to set a variable within the string. Example:
var malformedJsonString = "{unquotedName: 'single quoted value'}";
eval("var myParsedJsonObject = "+malformedJsonString+";");
// myParsedJsonObject now contains your parsed JSON object

Keys to Mongoose Object are different from property names

In order to troubleshoot an error I have been getting, I wrote the following snippet:
var myFunction = function(obj) {
var keys = Object.getOwnPropertyNames(obj);
console.log(obj);
console.log(keys);
}
When running my function within a mongoose query callback, the console logs this:
{_id: 5a8g123vjsdj83nf8afvn48,
username: 'Player1',
adv1: { name: 'a', type: '!' },
adv2: { name: 'a', type: '!' },
adv3: { name: 'a', type: '!' },
__v: 0,
invitations: [ 'PlayTest1', 'PlayTest2' ] }
[ '$__', 'isNew', 'errors', '_doc', '$init' ]
Now as far as I understand it, the last line in the console (separated for reading convenience) should read:
[ '_id', 'username', 'adv1', 'adv2', 'adv3', '__v', 'invitations ]
My question is why does the keys obj I create in myFunction not contain the properties names shown when I log the actual object?
Mongoose document fields are stored in obj._doc
console.log displays the document fields due to an .inspect function attached to the document object.
Use node --inspect if you want to debug something.

Find a Value in a Dictionary with a certain Property

Say I have a dictionary like
objDict = {
id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' },
}
If I wanted to search for the property "someId2" of the "id" property in Values of that dictionary.. how would I be able to grab the the whole object after finding it?
I really can't think of a good way to do it outside of iterating the dictionary with a for-in loop. I was thinking of using Object.values() but even then I can't think of a way of using that to grab the whole object that contains someId2.. I would only be able to determine if that property existed.
I was just wondering if there was a better way than a for-in loop. Thanks
you can just get all the keys using Object.keys(), then iterate over these and select the object you want based on the required id.
var objDict = {
id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' },
};
var obj;
Object.keys(objDict).forEach(x => obj = objDict[x].id === 'someId2' ? objDict[x]: obj);
console.log(obj);
There is Object.entries(), but for-in loop is more efficient than the other alternatives.
objDict = { id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' } }
item = Object.entries(objDict).find(a => a[1].id === 'someId2')
console.log(JSON.stringify(item))
The following code snippet containing break; statement could statistically provide a better performance vs. the selected one.
objDict = {
id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' },
};
var obj;
for (var key in objDict)
{
if (objDict[key].id==='someId2')
{
obj = objDict [key];
break;
}
}
console.log(obj);
ECMA Script 2015 provides another alternative of using Map object as described in : How to iterate (keys, values) in javascript?
Hope this may help.
This is a generic indexing problem, so no, there's no good way to do this other than looping through each value in the dictionary. This is because you've created a data structure to access the data quickly (O(1) time), but the drawback is that if you want to find the data through another index quickly, you'll have to search manually O(n) time for it.
If you really did care about the actual value of someId, then the canonical answer would be to create 2 separate dictionaries w/ the same data w/ different key values. This is very similar to having 2 indexes on a database table.

Categories

Resources