Split a string into sub-strings starting from " ] " ending with "As" - javascript

I have extracted a string from stored procedure in Microsoft SQL Server 2008 R2,
using this query:
EXEC sp_helptext 'MyStoredProcedureName'; I need to split this string into arrays or sub strings starting from the ending parenthesize " ] " and ending with the word "As".
I have to save ALL fields starting with "#" in (fieldsArray) and the types of these fields -After the space- in (typeArray) and then output them to a file in node.js with this format:
InvoiceNo: {
type: DataType.String(255),
},
Here is the extracted string:
CREATE PROCEDURE [dbo].[MyStoredProcedureName]
#InvoiceNo int
,#TransDate datetime
,#CustomerID bigint
,#CurrencyID bigint
,#SalesInvoiceTypeID bigint
,#DiscountAmount nvarchar(50)
,#DetailXml ntext
,#TotalAll float
,#TotalBefore float
,#TaxAmount float
,#OtherExpenses float
,#OutVouchersNo nvarchar(1000)
,#Notes nvarchar(1000)
,#TotalWiegts float
,#VoucherDefID bigint
,#SalesmanID bigint
,#IsSale bit
AS
BEGIN TRANSACTION
Edit: I used another query instead of the mentioned above. Now I have an object containing the result of the query which is the Names of the fields each in separate line. What I need to do now is to separate this object to a string array so that I can deal with each name separately.
the object "Names" contain the result of the query that I used:
Names = await sequelize.query(namesQuery);
And this is the output of console.log(Names);
[ [ { '': 'InvoiceNo' },
{ '': 'TransDate' },
{ '': 'CustomerID' },
{ '': 'CurrencyID' },
{ '': 'SalesInvoiceTypeID' },
{ '': 'DiscountAmount' },
I tried Names.split but I got the error "Names.split is not a function"

You could query the Sys.Parameters table instead:
SELECT Substring(Parameters.Name,2,255) + ': { type: DataType.' +
CASE types.Name
WHEN 'varchar' THEN 'String(' + CAST(Parameters.Max_Length As Varchar)
WHEN 'int' THEN 'Int'
ELSE 'COMPLETE THE REST OF THIS yourself.....'
END + '),},'
FROM Sys.Parameters
INNER JOIN sys.procedures on parameters.object_id = procedures.object_id
INNER JOIN sys.types on parameters.system_type_id = types.system_type_id AND parameters.user_type_id = types.user_type_id
Where procedures.name = 'MyStoredProcedureName'

If you consider that the symbol # is only used to identify the beginning of a field name, you can use regular expressions as follows:
let result = {}
str.match(/#\w+ [a-z]+/g)
.map(s => s.match(/#(\w+) ([a-z]+)/))
.forEach(r => {
let fieldName = r[1]
let type = r[2]
result[fieldName] = { type }
})
console.log(JSON.stringify(result, undefined, 2))
This will output
{
"InvoiceNo": {
"type": "int"
},
"TransDate": {
"type": "datetime"
},
...
}
I guess the same could be achieved with only regex, but the code is easier to understand this way:
the first match captures all the #FieldName type strings
the second match captures for each of these FieldName and type
the last one fills the object result with members of name FieldName and content { type: "type" }
the last command formats everything and outputs to the console

Related

check if string does not contain values

I am using indexOf to see if an email contains anything other than a particular text.
For example, I want to check if an email DOES NOT include "usa" after the # symbol, and display an error message.
I was first splitting the text and removing everything before the # symbol:
var validateemailaddress = regcriteria.email.split('#').pop();
Then, I check if the text doesn't include "usa":
if(validateemailaddress.indexOf('usa')){
$('#emailError').show();
}
Something with the above check doesn't seem right. It works - I can enter an email, and if it does not include 'usa', then the error message will show.
Regardless, when I add an additional check, like if the email does not include "can", then the error message shows no matter what.
As follows:
if(validateemailaddress.indexOf('usa') || validateemailaddress.indexOf('can')){
$('#emailError').show();
}
As stated, using the above, the error message will show regardless if the email includes the text or not.
All I want to do is check if the email includes 'usa' or 'can', and if it doesn't, then show the error message.
How can I make this work?
Here is a simple JavaScript function to check if an email address contains 'usa' or 'can'.
function emailValid(email, words) {
// Get the position of # [indexOfAt = 3]
let indexOfAt = email.indexOf('#');
// Get the string after # [strAfterAt = domain.usa]
let strAfterAt = email.substring(indexOfAt + 1);
for (let index in words) {
// Check if the string contains one of the words from words array
if (strAfterAt.includes(words[index])) {
return true;
}
}
// If the email does not contain any word of the words array
// it is an invalid email
return false;
}
let words = ['usa', 'can'];
if (!emailValid('abc#domain.usa', words)) {
console.log("Invalid Email!");
// Here you can show the error message
} else {
console.log("Valid Email!");
}
You can do something like that, using includes:
const validateEmailAdress = (email) => {
const splittedEmail = email.split('#').pop();
return (splittedEmail.includes('usa') || splittedEmail.includes('can'))
}
console.log("Includes usa: ", validateEmailAdress("something#gmail.usa"))
console.log("Includes can: ", validateEmailAdress("something#gmail.can"))
console.log("Does not includes: ", validateEmailAdress("something#gmail.com"))
There are several ways to check, if a string contains/does not contain a substring.
String.prototype.includes
'String'.includes(searchString); // returns true/false
String.prototype.indexOf
// returns values from -1 to last postion of string.
'String'.indexOf(searchString);
// In combination with ~ this can work similar to includes()
// for strings up to 2^31-1 byte length
// returns 0 if string is not found and -pos if found.
~'String'.indexOf(searchString);
With the help of Regular Expressions:
// substring must be escaped to return valid results
new RegExp(escapedSearchString).test('String'); // returns true/false if the search string is found
'String'.match(escapedSearchString); // returns null or an array if found
So overall you can use allmost all methods like:
if ('String'.function(searchString)) {
// 'String' includes search String
} else {
// 'String' does not include search String
}
Or in case of indexOf:
if ('String'.indexOf(searchString) > -1) {
// 'String' includes search String
} else {
// 'String' does not include search String
}
// OR
if (~'String'.indexOf(searchString)) {
// 'String' includes search String
} else {
// 'String' does not include search String
}
I believe this regular expression match is what you're looking for
System.out.println(myString.matches("(.)#(.)usa(.*)"));

What is the most elegant way to implement the requirements below if there is no multiple inheritance in javascript?

The system produces log records of 2 types:
LogsA and LogsB.
Write js function to create a record of type LogsC
from list of records of type logs_A and logs_B
logs_A:{
m_id: string
time: number
level: info|warning|error
msg: string
}
logs_B:
{
id: string
m_id:string
m_name:string
}
logs_C: {
m_id: string
timestamp: number
level: string
details: string
}
requirements:
join on m_id
details field in type C is combination of B.module_name + ' ' + A.msg
delete records of level in
sort by timestamp

How to convert BigQuery Struct Schema string to Javascript object?

I have extracted the schema of my BigQuery table from the "INFORMATION_SCHEMA" table. I get the list of all the columns in the table in a proper Javascript Object format except of the "Struct" and "Array" data types. I need a clean way to convert the "Struct" and "Array" string into a javascript object.
I am working with NodeJS v11.2 I have written a small regex which extracts the following. But it doesn't seem right to split the string and iterate through each word until I get the output. I need a cleaner way to solve this.
let structString = "STRUCT<name STRING, email STRING, time_sec INT64, tz_offset INT64, date STRUCT<seconds INT64, nanos INT64>>";
let _structSchema = structString.match(/STRUCT<([^)]+)>/)[1];
console.log(_structSchema); // name STRING, email STRING, time_sec INT64, tz_offset INT64, date STRUCT<seconds INT64, nanos INT64>
I need to write a recursive function which will parse though the string and give me the output in following manner.
{
"name": "STRING",
"email": "STRING",
"time_sec": "INT64",
"tz_offset": "INT64",
"date": {
"seconds": "INT64",
"nanos": "INT64"
}
}
The function should run irrespective of the depth/hierarchy of the nested structs/arrays.
Using a regex can be good to tokenise the input string, but you'll need more logic to perform the actual parsing.
Here is how you could do it:
function parse(structString) {
let tokenizer = /([a-z_]\w*)|\S|$/gi;
function next(identifier, expected) {
let match = tokenizer.exec(structString);
function error(expected) {
throw `Expected ${expected} but got ${match[0]} at ${match.index}`;
}
match[0] = match[0] || "<EOF>";
if (identifier && !match[1]) error(identifier);
if (expected && !expected.includes(match[0])) error(expected.join(" or "))
return match[0];
}
function getType() {
let fieldType = next("type identifier or STRUCT or ARRAY");
if (fieldType === "STRUCT") {
next(null, ["<"]);
fieldType = {};
do {
fieldType[next("field identifier")] = getType();
} while (next(null, [",", ">"]) === ",");
} else if (fieldType === "ARRAY") {
next(null, ["<"]);
fieldType = [getType()];
next(null, [">"]);
}
return fieldType;
}
let result = getType();
next(null, ["<EOF>"]);
return result;
}
// Sample input & call
let structString = "STRUCT<name STRING, email STRING, time_sec INT64, tz_offset INT64, date STRUCT<seconds INT64, nanos INT64>, phones ARRAY<STRING>>";
let obj = parse(structString);
console.log(obj);
If you can access Google Cloud Console from your environment, consider running something like: bq show --format=pretty project:dataset.table or bq show --format=prettyjson project:dataset.table. You would still need to parse the results for your purposes, but the nesting is already done for you.

Sequelize query string prefix / starts with

I'm building an autofill function that takes a string input and returns a list of string suggestions.
Sequelize's iLike:query returns every string in which the queried string appears. I would like to favour strings for which the query is a prefix. For example when query='sh' then the results should return strings that start with sh instead of having sh anywhere within the string.
This is relatively simple to do after receiving the data from the DB, however I was wondering if there is a way to accomplish this via sequelize while querying the DB? If so how?
The DB size will be between 10,000 and 100,000 strings of no more than a handful of words (company names to be exact).
Optional question: DB's usually have superior performance to generically written code, in this circumstance should there even be a noticeable difference? Or should I just collect all the data from the DB and apply some other filters on it after via vanilla JS.
let suggestions = yield db.Company.findAll({
limit: 7,
where: {
company_name: {
$iLike: '%'+this.data.query
}
}
})
Seems like this is super easy! The '%' acts like a * from regex. So query + '%' returns any results where query is the prefix.
let suggestions = yield db.Company.findAll({
limit: 5,
where: { $or: [
{ stock_ticker: { $ilike: query + '%' } },
{ company_name: { $ilike: query + '%' } }
]},
order: '"volume" DESC'
})

How to get Abstract Syntax Tree (AST) out of JISON parser?

So I have generated a parser via JISON:
// mygenerator.js
var Parser = require("jison").Parser;
// a grammar in JSON
var grammar = {
"lex": {
"rules": [
["\\s+", "/* skip whitespace */"],
["[a-f0-9]+", "return 'HEX';"]
]
},
"bnf": {
"hex_strings" :[ "hex_strings HEX",
"HEX" ]
}
};
// `grammar` can also be a string that uses jison's grammar format
var parser = new Parser(grammar);
// generate source, ready to be written to disk
var parserSource = parser.generate();
// you can also use the parser directly from memory
// returns true
parser.parse("adfe34bc e82a");
// throws lexical error
parser.parse("adfe34bc zxg");
My question is, how do I retrieve the AST now? I can see that I can run the parser against input, but it just returns true if it works or fails if not.
For the record, I am using JISON: http://zaach.github.com/jison/docs/
I discovered an easier and cleaner way than the one in the other answer.
This post is divided into 2 parts:
General way: Read how to implement my way.
Actual answer: An implementation of the previously described way specific to OP's request.
General way
Add a return statement to your start rule.
Example:
start
: xyz EOF
{return $1;}
;
xyz is another production rule. $1 accesses the value of the first symbol (either terminal or non-terminal) of the associated production rule. In the above code $1 contains the result from xyz.
Add $$ = ... statements to all other rules.
Warning: Use $$ = ..., don't return! return will immediately abort further execution by returning the specified value, as the name indicates.
Example:
multiplication
: variable '*' variable
{$$ = {
type: 'multiplication',
arguments: [
$1,
$3
]
};
}
;
The above production rule will pass the object $$ to the higher level (i.e. the production rule which used this rule).
Let's complement the multiplication rule in order to achieve a runnable example:
/* lexical grammar */
%lex
%%
\s+ /* skip whitespace */
[0-9]+("."[0-9]+)?\b return 'NUMBER'
[a-zA-Z]+ return 'CHARACTER'
"*" return '*'
<<EOF>> return 'EOF'
. return 'INVALID'
/lex
%start start
%% /* language grammar */
start
: multiplication EOF
{return $1;}
;
multiplication
: variable '*' variable
{$$ = {
type: 'multiplication',
arguments: [
$1,
$3
]
};
}
;
variable
: 'NUMBER'
{$$ = {
type: 'number',
arguments: [$1]
};
}
| 'CHARACTER'
{$$ = {
type: 'character',
arguments: [$1]
};
}
;
You can try it online: http://zaach.github.io/jison/try/. At the time of this edit (12.02.2017), the online generator sadly throws an error - independently of the Jison file you feed in. See the addendum after step 3 for hints on how to generate the parser on your local machine.
If you input for example a*3, you get the object structure below:
{
"type": "multiplication",
"arguments": [
{
"type": "character",
"arguments": ["a"]
},
{
"type": "number",
"arguments": ["3"]
}
]
}
Clean the code and generated AST by injecting custom objects
When using the Jison-generated parser, you can inject arbitrary objects into the scope of the 'code blocks' in the syntax file:
const MyParser = require('./my-parser.js');
MyParser.parser.yy = {
MultiplicationTerm
/*, AdditionTerm, NegationTerm etc. */
};
let calculation = MyParser.parse("3*4");
// Using the modification below, calculation will now be an object of type MultiplicationTerm
If MultiplicationTerm had a constructor accepting both factors, the new part for multiplication would look like this:
multiplication
: variable '*' variable
{$$ = new yy.MultiplicationTerm($1, $3);}
;
Addendum on how to create the Jison parser:
Download the Jison NPM module. Then you can create the Jison-parser either by using Jison's command-line or running new jison.Generator(fileContents).generate() in your build file and write the returned string to your preferred file, e.g. my-parser.js.
Actual answer
Applying the rules above leads to the Jison file below.
The Jison file format and the JavaScript API (as stated in the question) are interchangeable as far as I know.
Also note that this Jison file only produces a flat tree (i.e. a list) since the input format is only a list as well (or how would you nest concatenated hex strings in a logical way?).
/* lexical grammar */
%lex
%%
\s+ /* skip whitespace */
[a-f0-9]+ return 'HEX'
<<EOF>> return 'EOF'
. return 'INVALID'
/lex
%start start
%% /* language grammar */
start
: hex_strings EOF
{return $1;}
;
hex_strings
: hex_strings HEX
{$$ = $1.concat([$2]);}
| HEX
{$$ = [$1];}
;
I'm not too familiar with Jison's inner workings, so I don't know any method that would do it.
But in case you're interested in a little bruteforce to solve this problem, try this:
First, create an object to hold the AST
function jisonAST(name, x) { this.name = name; this.x = x; }
// return the indented AST
jisonAST.prototype.get = function(indent){
// create an indentation for level l
function indentString(l) { var r=""; for(var i=0;i<l;i++){r+=" "}; return r }
var r = indentString(indent) + "["+this.name+": ";
var rem = this.x;
if( rem.length == 1 && !(rem[0] instanceof jisonAST) ) r += "'"+rem[0]+"'";
else for( i in rem ){
if( rem[i] instanceof jisonAST ) r += "\n" + rem[i].get(indent+1);
else { r += "\n" + indentString(indent+1); r += "'"+rem[i]+"'"; }
}
return r + "]";
}
Add a little helper function for Jison's BNF
function o( s ){
r = "$$ = new yy.jisonAST('"+s+"',[";
for( i = 1; i <= s.split(" ").length; i++ ){ r += "$"+i+"," }
r = r.slice(0,-1) + "]);";
return [s,r];
}
With this, continue to the example code (slight modification):
var Parser = require("jison").Parser;
// a grammar in JSON
var grammar = {
"lex": {
"rules": [
["\\s+", "/* skip whitespace */"],
["[a-f0-9]+", "return 'HEX';"]
]
},
"bnf": {
// had to add a start/end, see below
"start" : [ [ "hex_strings", "return $1" ] ],
"hex_strings" :[
o("hex_strings HEX"),
o("HEX")
]
}
};
var parser = new Parser(grammar);
// expose the AST object to Jison
parser.yy.jisonAST = jisonAST
Now you can try parsing:
console.log( parser.parse("adfe34bc e82a 43af").get(0) );
This will give you:
[hex_strings HEX:
[hex_strings HEX:
[HEX: 'adfe34bc']
'e82a']
'43af']
Small note: I had to add a "start" rule, in order to only have one statement that returns the result. It is not clean (since the BNF works fine without it). Set it as an entry point to be sure...

Categories