regex to get all JavaScript variable names and values from string - javascript

say I have the following string:
let code = `
var x = 4;
var y=9,
w=8
var z=8080
other()
x=12
`
How do I write a regex that gets all of the variable declarations? In this case, I want to return all statements, and no expressions, so exclude the other() and x=12 part. So far I have
let results = `
var x = 4;
var y=9,
w=8
var z=8080
other()
x=12
`.match(/(var)(.*?)(;|,|\n)/g);
console.log(results);
But I couldn't figure out how to include the w=8 part also, since that's also a declaration statement, while excluding the expressions. I tried .match(/(var|\n)(.*?)(;|,|\n)/g) but that also returns x=12, which I don't want. I need to also return all statement blocks even if there is a comma right before it (either on a previous line, or previous character, anything that would normally allow a variable declaration).
Any idea how to do this with regex?
EDIT:
with uglifyjs I'm able to do the following (which is the result I want):
let strin = `
var x = 4;
var y=9,
w=8,
kk = {
ok:1234
},
p,
a = undefined
var z=8080
other()
x=12
`
let results = UglifyJS.parse(strin).body.filter(x=>x.__proto__.TYPE == "Var")
.map(x=>
x.definitions.map(y=>({
name:y.name.name,
value:y.value ? findInPos(y.value.start, y.value.end,strin) : undefined
})),
).flat()
function findInPos(start, end,str) {
return str.substring(
start.pos,
end.endpos
);
}
<script type="text/javascript" src="https://yaakovyitzchak.github.io/coby/uglify.js">
</script>
But it seems a bit overkill just to extract the string value of each variable? (I don't need to evaluate it, just get the string value like so)

The task can be nicely solved with TypeScript Compiler API, as it can parse not only TypeScript but JavaScript code too. Since TypeScript compiles into JavaScript - there should be no problem to integrate the following TS snippet into a JS project.
See demo
import ts from "typescript";
const code = `
var x = 4;
var y=9,
w=8
var z=8080
other()
x=12
`;
const sf = ts.createSourceFile(
"test.js",
code,
ts.ScriptTarget.ES2017,
true,
ts.ScriptKind.JS,
);
sf.forEachChild((n) => {
if (ts.isVariableStatement(n)) {
n.declarationList.declarations.forEach((decl) => {
const name = decl.name.getText();
const value = decl.initializer?.getText();
console.log(`${name} = ${value}`);
});
}
});
The code above prints the following output to console:
x = 4
y = 9
w = 8
z = 8080

Related

Get payload from String

I have this String:
['TEST1-560', '{"data":[{"price":0.0815,"volume":0.2,"car":"BLUE"}],"isMasterFrame":false}']
I want to get the keys 'TEST1-560' which is always fist and "car" value.
Do you know how I can implement this?
This is a very, very scuffed code, but it should work for your purpose if you have a string and you want to go through it. This can definitely be shortened and optimized, but assuming you have the same structure it will be fine.:
// Your data
var z = `['TEST1-560', '{"data":[{"price":0.0815,"volume":0.2,"car":"BLUE"}],"isMasterFrame":false}']`;
var testName = z.substring(2).split("'")[0];
var dividedVar = z.split(",");
for (var ind in dividedVar) {
if (dividedVar[ind].split(":")[0] === '"car"') {
var car = dividedVar[ind].split(":")[1].split("}")[0].substring(1,dividedVar[ind].split(":")[1].split("}")[0].length-1);
console.log(car)
}
}
console.log(testName);
output:
BLUE
TEST1-560
In a real application, you don't need to log the results, you can simply use the variables testName,car. You can also put this in a function if you want to handle many data, e.g.:
function parseData(z) {
var testName = z.substring(2).split("'")[0];
var dividedVar = z.split(",");
for (var ind in dividedVar) {
if (dividedVar[ind].split(":")[0] === '"car"') {
var car = dividedVar[ind].split(":")[1].split("}")[0].substring(1, dividedVar[ind].split(":")[1].split("}")[0].length - 1);
}
}
return [testName, car]
}
This will return the variables values in an array you can use
const arr = ['TEST1-560', '{"data":[{"price":0.0815,"volume":0.2,"car":"BLUE"}],"isMasterFrame":false}']
const testValue = arr[0];
const carValue = JSON.parse(arr[1]).data[0].car;
console.log(testValue);
console.log('-----------');
console.log(carValue);
If your structure is always the same, your data can be extracted like above.

sum object from multi arrays in app script

I'm using app script
I have Return array from API by this code :
const price= jsonResponce.price.map(obj => [obj[0],obj[1]]);
Give me [[30.56, 1.014], [50.44, 1.019], [10.35, 1.081], [10.34, 1.115], [10.40, 2.006]]
Not this array can be has 1000 array or large
Now I want to sum all object in obj[0] by using this code :
I use to method to see the deference but nothing work
var first= [];
var second= 0;
price.forEach(function(obj){
first+= obj[0];
second+= obj[1];
});
Logger.log(first);
Logger.log(second);
But Give me result like that: first Logger.log(first);
30.5650.4410.3510.3410.40
second Logger.log(second); : this method add number 0 after any obj
01.01401.01901.08101.11502.006
Any idea for this problem
30.56+50.44+10.35+10.34+10.40
I need result as : 112.09
Your code works fine for me, after minimal corrections:
const price = [[30.56, 1.014], [50.44, 1.019], [10.35, 1.081], [10.34, 1.115], [10.40, 2.006]]
var first = 0; // <-- here
var second = 0;
price.forEach(obj => {
first += +obj[0]; // <-- here
second += +obj[1]; // <-- here
});
console.log(first); // --> 112.09
console.log(second); // --> 6.2349
You can get four digits after dot this way:
var a = 1.23456789;
var b = 1234.56789;
var c = 16643.59000000003
const dot1000 = x => Math.round(x*10000)/10000;
console.log(dot1000(a));
console.log(dot1000(b));
console.log(dot1000(c));
Another implementation (with zeros at the end)
var a = 1.23456789
var b = 1234.56789
var c = 16643.59000000003
const dot1000 = x => parseInt(x) + '.' + (x+.00001+'').split('.')[1].slice(0,4)
console.log(dot1000(a))
console.log(dot1000(b))
console.log(dot1000(c))
Update
Modern JavaScript (ES2017) can add zeros this way:
console.log('123'.padEnd(5,'0')); // 12300
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
numbersInArray = price.flat()
let sum = 0
numbersInArray.forEach( number => sum += number)
Using Array.prototype.reduce() method, this becomes very easy:
const result = arr.reduce((total, el)=> {
return total + el[0] + el[1] // el = [a, b]
}, 0)
console.log(result) /// 118.325
Let me know in the comments if this is what you want or you want any improvements
You are adding the array in which each number (both obj[0] and obj[1] values) is treated as String type. So that's why they are not added like numbers but concatenated. First convert the String Type into Number. Then your problem will be resolved.
As I don't know about the used API. So I could not give answer with API response included. But I am giving you code where I do some changes but just look at the praseFloat() method that I used.
const Api_Response_Array = [["30.56", "1.014"], ["50.44", "1.019"], ["10.35", "1.081"], ["10.34", "1.115"], ["10.40", "2.006"]];
var first= 0;
var second= 0;
Api_Response_Array.forEach(function(){
first+= parseFloat(Api_Response_Array[0][0]);
second+= parseFloat(Api_Response_Array[0][1]);
});
document.write(first + "<br>");
document.write(second);
Just use praseFloat method inside function used within forEach loop. It will convert the string into number.

How to pass dictionary inside template literal in JS

How do I pass the whole dictionary inside the template literal?
Here is my code:
var pvtInPlan = treatmentPlan.pavementIDs;
var pcrAfterPlan = treatmentPlan.pavementCondition;
var yearlyPlan = {};
pvtInPlan.forEach((key, i) => yearlyPlan[key] = pcrAfterPlan[i]); // I want to pass this yearlyPlan
var arcadeExpression = `
var plan = ${yearlyPlan};
var pvtID = 100;
return plan[pvtID]`; // I want to be able to return such statement.
Whenever I use 'var plan = ${yearlyPlan};' line, it throws me error. It works when I use 'var plan = ${yearlyPlan[100]};' directly. But I need to pass index to this dictionary from inside of template literal.
I would be glad if someone could help me with this.
Thanks!
You can just do a simple JSON.stringify if u want to dump the entire content, for example:
const yearlyPlan = JSON.stringify({ key1: 'content', key2: 'content2' })
const arcadeExpression = `
var plan = ${yearlyPlan};
var pvtID = 100;
return plan[pvtID]`; // I want to be able to return such statement.
console.log(arcadeExpression)
>>>
"var plan = {"key1":"content","key2":"content2"};
var pvtID = 100;
return plan[pvtID]"
If you want a more customized version, then u would need to access each key-value pair to format the message.

Retrieve value of variables from a string of declarations

I have the following string named abc (which consists of javascript variables declarations):
var abc = `
var exp = 'test';
var test = 15;
var test2 = "wow";
`;
I would like to get the value of exp, test, test2 from this string.
One of the methods that might work is:
eval(abc);
However this solution is not suitable in typescript for security purposes, what other methods would you recommend ( feel free to propose some npm libraries) ?
Creating a function and passing the variable.
new Function has a closed scope in comparison to function eval.
var abc = `
var exp = 'test';
var test = 15;
var test2 = "wow";
`;
var fn = (variable) => (new Function('', `${abc} return ${variable};`))();
console.log(fn('exp'));
console.log(fn('test'));
console.log(fn('test2'));
One way to do that would be the following:
First we find the declarations by splitting the string using ; as delimiter
Now after we have found the declarations we use map() to extract the values of each one and return it into a new array.To find the value of a declaration we split it into 2 parts using '='.The first part is the declaration name and the second part is the declaration value
var abc = `var exp = 'test';
var test = 15;
var test2 = "wow";`;
//Trim string
abc = abc.trim();
//Split by ';'
var declarations = abc.split(';');
//The last item is empty so we remove it
declarations.pop();
console.log(declarations)
//Now that we have the declarations we need to get the values
//so we split each declaration using '='
var values = declarations.map(function(dec){
var value = dec.split("=").pop()
return value;
});
//Bonus
//This gets all declarations from a string and returns them as a key value object
function getDeclarations(s) {
var variables = s.split(";");
//Last variable is an empty string so pop it
variables.pop()
var declarations = {};
variables.forEach(function (variable) {
variable = variable.trim()
var name = variable.split("=")[0].split("var")[1];
var value = variable.split("=").pop();
name = name.trim();
value = value.trim();
declarations[name] = value;
});
return declarations;
}
console.log(values)
console.log(getDeclarations(abc))

is there a way to use newlines in xdmp.eval()

Hi I want to write clean code that I can read and have a good overview.
So I wrote this:
var id = '12345';
var coll = ['scc-roles','scc-proj-' + id];
var spm = 'some-role';
var data = {role : spm, roleNames : 'sccss-user', collection : coll}
var spmRoleId = xdmp.eval('declareUpdate();
var sec = require("/MarkLogic/security.xqy");
var roleId = sec.createRole(role, "Generated project member", roleNames, null, collection,null,null);
var uri = "http://marklogic.com/xdmp/roles/" + roleId;
xdmp.documentAddCollections(uri,collection)',data,{"database" : xdmp.securityDatabase()})
But apparently a newline is not allowed in xdmp.eval() ?
[javascript] JS-JAVASCRIPT: + 'var sec = require("/MarkLogic/security.xqy"); -- Error running JavaScript request: SyntaxError: Unexpected token ILLEGAL
I tried using a '+' sign to generate a strng over more then one line, swapping single and double quotes but no luck.
Being able to test this code (copy paste) to the security database makes a lot of sense to me...
If I wrap it all in one unreadable line , it works ok.
hugo
The way to effectively create a new line in a JavaScrit string is to escape the new line char like this
var str = "I'm displayed\
in two line";
In the final file, you will see effectively a new line.
If you want see in the dist output the new line but not in your src string you could just insert the \n equivalent of a return to line.
var str = "I'm displayed\n in two line";
In es6 you will be able to use ` char to achieve the same thing without \
var str = `I'm displayed
in two line`;
Maybe you would like the strange, yet useful array-notation way of doing this:
var multiline1 = [
'the lazy fox',
'jumped over',
'the dead chicken',
].join('\n');
and the result:
the lazy fox
jumped over
the dead chicken
In general, you should avoid string concatenation to build code for eval. Strings make it difficult to spot bugs and are a great vector for injection attacks. Instead, I'd advise you to write a proper function in XQuery or JavaScript and use xdmp.invokeFunction to evaluate it. invokeFunction takes all of the same options as xdmp.eval.
Here's an example that gets roles in the context of a security database. The applyAs function returns a function that wraps the function provided by the caller, evaluating it with the eval options provided.
function applyAs(fct, options) {
return function() {
var params = Array.prototype.slice.call(arguments);
// Curry the function to include the params by closure.
// xdmp.invokeFunction requires that invoked functions have
// an arity of zero.
var f = (function() {
return fct.apply(null, params);
}).bind(this);
// Allow passing in user name, rather than id
if(options.user) { options.userId = xdmp.user(options.user); delete options.user; }
// Allow the functions themselves to declare their transaction mode
if(fct.transactionMode && !(options.transactionMode)) { options.transactionMode = fct.transactionMode; }
return xdmp.invokeFunction(f, options); // xdmp.invokeFunction returns a ValueIterator
}
}
/**
* Gets an Array of id-name Objects. Requires privileged access to security.
*
* #param names An optional Array of role IDs as strings used to filter
* #return An Array of Objects with role ID keys and role name values
*/
function getRoles(names) {
var sec = require('/MarkLogic/security.xqy');
var db = {database: xdmp.securityDatabase()};
var roleIDs = applyAs(sec.getRoleIds, db);
var rolesItr;
if(Array.isArray(names)) {
rolesItr = roleIDs(xdmp.arrayValues(names));
} else {
rolesItr = roleIDs();
}
var roleNames = applyAs(sec.getRoleNames, db)(rolesItr).toArray().map(function(el) { return el.textContent; });
var roles = [];
var i = 0;
for(var role of rolesItr) {
var r = {}
r[role.textContent] = roleNames[i++];
roles.push(r);
}
return roles;
}
getRoles();
Originally from a gist.

Categories