I'm trying to get away from using horrible switch cases in node.js. I am looking for a more efficient way of testing an input against various regex cases. Dependent on the case that is matched I either fire an event or I do some transformation of the input before running another function.
To save having a really long block of code I have cut down my function to the skeleton below so it shows a focus on the switch.
I've taken a look at the possibility of using .map to return a true false but I'm unsure how best to implement that also.
Any advise or suggestions on the best way to do this?
function ParseLogMessages(message, config, callback){
var _this = this;
try {
//Define regex in order to match strings based on case
_this.to_group = new RegExp("^\\[\\d{2}:\\d{2}\\]\\s+\\w+\\s+tg+\\s\\>{3}");
_this.from_group=new RegExp("^\\[\\d\\d:\\d\\d\\]\\s\\w+\\s\\w+\\s\\>{3}");
_this.to_person = new RegExp("^\\[\\d{2}:\\d{2}\\]\\s[a-zA-Z0-9 \\- _]+\\s\\<{3}.+");
_this.from_person = new RegExp("^\\[\\d{2}:\\d{2}\\]\\s\\w+\\s\\>{3}");
_this.contact = new RegExp("(User #+\\d+:)");
_this.contact = new RegExp("(User #+\\d+:)");
//Test message against each to find type
switch (true){
//Message sent to a group chat
case _this.to_group.test(_this.payload.raw):
break;
//Message from a group chat
case _this.from_group.test(_this.payload.raw):
break;
//Message sent to a person from the bot
case _this.to_person.test(_this.payload.raw):
break;
//Message sent from a person to the bot
case _this.from_person.test(_this.payload.raw):
break;
//Contact shared
case _this.contact.test(_this.payload.raw):
break;
default:
break;
}
callback(null,"Logfile message parsed ok!");
} catch(err) {
log.error(err);
return callback(err,null);
}
}
You can create an array of regex/function pairs and loop through the array:
_this.tests = [
{ regex: new RegExp("^\\[\\d{2}:\\d{2}\\]\\s+\\w+\\s+tg+\\s\\>{3}"), // to_group
action: ... // action for to_group
},
{ regex : new RegExp("^\\[\\d\\d:\\d\\d\\]\\s\\w+\\s\\w+\\s\\>{3}"), // from_group
action: ... // action for from_group
},
// etc.
];
Then you can loop through the array, testing, and breaking when the test works:
for (i=0; i<tests.length; ++i) {
if (tests[i].regex.test(_this.payload.raw) {
tests[i].action();
break;
}
}
You can put the objects in an array and call the test function until one returns true:
var o = [
_this.to_group,
_this.from_group,
_this.to_person,
_this.from_person,
_this.contact
];
for (var i in o) {
if (o[i].test(_this.payload.raw)) {
// got a match
break;
}
}
What you want is to convert that into an associative array and match with a loop. Untested code that should work:
let patterns = {
"^\\[\\d{2}:\\d{2}\\]\\s+\\w+\\s+tg+\\s\\>{3}": funcToGroup /* code for this case, preferably a [reference to a] function object without the parens */,
"^\\[\\d\\d:\\d\\d\\]\\s\\w+\\s\\w+\\s\\>{3}": function () {
// An inline anonymous function is also fine
},
"^\\[\\d{2}:\\d{2}\\]\\s[a-zA-Z0-9 \\- _]+\\s\\<{3}.+": funcToPerson,
"^\\[\\d{2}:\\d{2}\\]\\s\\w+\\s\\>{3}": funcFromPerson,
"(User #+\\d+:)": funcContactShared
};
for (let pat in _this.patterns) {
if (new RegExp(pat).test(_this.payload.raw)) {
_this.patterns[pat](); // Actually execute the relevant case
}
}
That should handle all the code within the try block.
Related
var checking_location = "none"
const getentitiesByType = (arr, type) => {
for (let i in arr) {
if (arr[i].type === type) {
checking_location = "exists"
return arr[i].entity
}
}
return null;
}
if (!meeting.location) {
if (checking_location != 'exists') {
rl.question('where is the location ', function(answer) {
// session.send("The location you gave:" answer);
rl.close();
session.send(answer)
// console.log(tryagain(answer, 'Calendar.Location'));
session.send(tryagain(answer, 'Calendar.Location'));
});
}
} else {
next();
}
What i'm trying to do here is to have a loop in the if (!meeting.location) if checking_location stays equal to none. Basically i want to check if a certain Json field exists, if it doesn't i want to keep asking the question in rl.question.My issues is that the code is only working the first time, then even if i give another input not containing the required field i don't get that question.Also note that this is not the entire code but it's more than enough to understand the possible issue spots in my implementation.
getentitiesByType needs to be called somewhere, simply assigning it to a variable will not make the function run: getentitiesByType(youArr, yourType).
Also, as a side note, instead of using string values for checking_location just rename the variable and use a boolean value. Ex: var hasLocation = false.
My script takes CSV input and from that finds a user's name. It then creates a URL given the user's name.
From there, the script opens the user's URL, collects some data about the user, and puts that info into an array for later output.
My problem is with the window.document.addEventListener. the specific code line is as follows:
element.document.addEventListener("DOMContentLoaded",getSomething(),false);
The strange behavior is as follows:
With the statement above, the listener fires and getSomething() code begins execution. However, the page is not loaded. in the console I can see that the page contents are simply nothing more than an empty body.
Changing "getSomething()" to "getSomething" (in the addEventListener code line) causes the pages to eventually load, however, the getSomething function is never executed (apparently addEventListener did not fire.)
some introduction to the code that follows:
variable testURLs is an array containing a user's URL.
function controlOpenWindows() is not fully set up but its intent is to determine when a window is ready to close, and when data from as many as four opened windows is collected, all four will close and four more will open. four is arbitrary. there are over 900 user URLs so just limiting number open at any one time.
The function that closes the previously opened windows makes the call to open more windows.
Please note that you would need a login id and password to open specific user pages. so passing the URL to you in this post would not be helpful. I'm hoping you can help without that specific info.
function closeOpenedWindow(index){
switch (index) {
case 0:
blnZero=true;
break;
case 1:
blnOne=true;
break;
case 2:
blnTwo=true;
break;
case 3:
blnThree=true;
}
if (blnZero===true && blnOne===true && blnTwo===true && blnThree===true) {
for (p=0; p<4; p++) {
openedWindow[p].close();
count +=1;
controlOpenWindows();
}
}
}
function controlOpenWindows() {
debugger;
testURLs=[];
blnZero=false;
blnOne=true;
blnTwo=true;
blnThree=true;
if (editorProfileURL.length>=4) {
testURLs[0]= editorProfileURL.shift();
testURLs[1] =editorProfileURL.shift();
testURLs[2]=editorProfileURL.shift();
testURLs[3]=editorProfileURL.shift();
} else {
for (n=0; n<editorProfileURL.length; n++) {
testURLs[n]=editorProfileURL[n];
}
}
testURLs.forEach(openWindow);
}
controlOpenWindows();
function openWindow(element1, index1, array1) {
openedWindow[index1]= window.open(element1);
}
function loaded(element, index, array) {
element.document.addEventListener("DOMContentLoaded", getSomething(), false);
}
openedWindow.forEach(loaded);
function getSomething() {
debugger;
var whichPage=this.document.URL;
function whichIndex(element, index, array) {
if (element.document.URL==whichPage) {
return element.document.URL;
}
}
var foundIndex=openedWindow.findIndex(whichIndex);
var reg=/The page you were looking for doesn*/g;
if (openedWindow[0].document.getElementsByClassName("container not-found").length>0) {
if(openedWindow[foundIndex].document.getElementsByClassName("container not-found")[foundIndex].innerHTML.match(reg)) {
closeOpenedWindow(foundIndex);
}
} else {
var firstEdit=openedWindow[foundIndex].document.getElementsByClassName("user-last-edit")[0].innerHTML;
var lastEditDaysAgo=openedWindow[foundIndex].document.getElementsByClassName("transaction-header-time")[0].innerHTML;
var rank=openedWindow[foundIndex].doucment.getElementsByClassName("user-rank")[0].innerHTML;
var editCount=openedWindow[foundIndex].document.getElementsByClassName("user-stats-value")[1].innerHTML;
updatedEditorInfo.push();
updatedEditorInfo.push(firstEdit + ",");
updatedEditorInfo.push(lastEditDaysAgo+ ",");
updatedEdtiorInfo.push(rank + ",");
updatedEditorInfo.push(editoCount + ",");
updatedEditorInfo.push("\n");
console.log(updatedEditorInfo);
alert(updatedEditorInfo);
//closeOpenedWindow();
//controlOpenWindows();
}
closeOpenedWindow(foundIndex);
controlOpenWindows();
}
Thanks for taking a look.
You need to pass a reference to the function instead of calling it directly:
element.document.addEventListener("DOMContentLoaded",getSomething,false);
Background Information
I'm trying to write some javascript / node.js code that will do the following:
Query a redis database and get back a bunch of keys from my hash. This is what the command looks like on the redis-cli:
127.0.0.1:6379> hkeys widgets:1231231234
1) "13:00:00_17:00:00_mtwrf"
2) "00:00:00_00:00:00"
3) "08:00:00_12:00:00_mtwrf"
or
127.0.0.1:6379> hkeys widgets:2222222222
1) "00:00:00_00:00:00"
Each widget will have at least one key... called the default key. Default keys look like this: "00:00:00_00:00:00"
For each HKEYS query, if there's more than one result returned, I need to test each key returned (except the default) against a set of match criteria. Whichever key matches, is what is used to do a subsequent HGET against redis. So for example, in the first result set shown above... If key 3 was a match I would run the following command:
127.0.0.1:6379> hget widgets:2222222222 08:00:00_12:00:00_mtwrf
"some value"
127.0.0.1:6379>
If neither key 1 or 3 matches, then I query for the default key.
Code
I've recently discovered the async module. I'm currently using it in my code to loop through the results from HKEYS.
Please see the code below:
async.map Code
router.get('/:widgetnum', function(req, res, next) {
//validate widgetnum format
var widgetnum = req.params.widgetnum;
if ( !valid_widget(widgetnum) ) {
var retval = {"res":false, "msg":"malformed widgetnum"};
res.send(JSON.stringify(retval));
} else {
var keys = {};
redis.hkeys("widgets:" + widgetnum, function(err, data){
if (err) {
res.send(JSON.stringify(false));
}
if (data) {
current = getCurrentUTC(); // needed by iterator for match criteria. Using a global variable for now.
async.map(data, hash_iterator, function (err, iterator_results) {
if (iterator_results) {
console.log("it are: " + iterator_results);
}
res.send(iterator_results);
}); //end async map
}
}); //end redis.hkeys
}
});
Question / Problem
This is working in the sense that for each key returned by HKEYS, I'm able to run the "hash_iterator" function.
However, once inside the iterator function, after evaluation / running my match criteria on each result, I don't have all the information I need to run the secondary HGET query.
The code:
async.map(data, hash_iterator, function (err, iterator_results)
passes just the values:
"13:00:00_17:00:00_mtwrf"
"00:00:00_00:00:00"
"08:00:00_12:00:00_mtwrf"
But I need the hash name (in this case "widgets:" ) and the widgetnum to make the HGET call.
I guess I can use global variables... but I wanted to make sure that my approach in general is correct here.
Any input would be appreciated.
Here's the hash_iterator logic:
var hash_iterator = function (redis_key, doneCallBack) {
var results = {};
console.log("processing..." + redis_key);
//skip if default rule....
if (redis_key.indexOf('00:00:00_00:00:00') == -1){
//need to write logic here to do HGET and save
//results somewhere... in case no other keys match
} else {
// run some logic to test match criteria.
if (matchfound) {
console.log("bingo. found match in " + redis_key);
redis.hget(hashname + widgetnum + redis_key, function (e, results){
if (results) {
return doneCallBack(null, results);
} else {
return doneCallBack(null, null);
}
});
}else {
console.log ("no match");
}
}
return results;
}
You can solve it in functional style with map. Just generate array of pairs and then pass it to async.map, for example
from [1,2,3] you can generate [['key', '1'], ['key', '2'], ['key', 3]]
So here the code, that generating pairs:
redis.hkeys("widgets:" + widgetnum, function(err, data){
// some code goes here
var pairs = data.map(function(ts) {
return [`widgets:${widgetnum}`, ts];
});
// async.map call goes here
}); //end redis.hkey
Inside async.map iterator callback you can access key with iterator_results[0]
But in your case the variable widgetnums is not global variable. It's just variable from upper scope and you can use it inside nested functions if you define it in propper scope. It's very useful trick that called lexical closure.
I'm running a script on an apache webserver on a linux box. Based on the parameter I want to change the name of variable(or set it)
The idea is that humDev(lines 11 and 14) is named humDev21 for example. Where devId is the number 21 in this example.
My script looks like this:
function getHumDev(devId){
$.ajax({
async: false,
url: "/url" + devId,
success: function(result) {
var array = result["Device_Num_" + devId].states;
function objectFindByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
humDev = array[i].value;
}
}
return humDev;
};
objectFindByKey(array, 'service', 'some');
}
});
};
If Im looking in the wrong direction, please do let me know. Maybe its bad practice what Im trying. The reason I want to have the object a unique name is because this function is called several times by another function, based on the content of an array. But when I have the humDev object named without the number suffix to make it unique, the content of the object is getting mixed up between the different calls.
I may be off base but I am making some assumptions based on what I understand of what you are trying to do.
First, you need to understand how to do file I/O in node.js. So lets start there:
var pathToFile, //set with file path string
fs = require('fs'), //require the file i/o module API
bunchOfHumDevs = {},
fileContents; //we'll cache those here for repeated use
fs.readFile(pathToFile, function(err, result) {
if (err) {
throw new Error(); //or however you want to handle errors
} else {
fileContents = JSON.parse(result); //assumes data stored as JSON
}
});
function getHumDev(devId) {
//first make sure we have fileContents, if not try again in 500ms
if (!fileContents) {
setTimeout(function() {
getHumDev(devId);
}, 500);
} else {
var array = fileContents["Device_Num_" + devId].states,
i = array.length,
//if 'service' and 'some' are variable, make them params of
//getHumDev()
while (i--) {
if (array[i]['service'] === 'some') {
//store uniquely named humDev entry
bunchOfHumDevs['humDev' + devId.toString()] = array[i].value;
break; //exit loop once a match is found
}
}
}
return null;
}
getHumDev(21);
assuming a match is found for the devId 21, bunchOfHumdevs will now have a property 'humDev21' that is the object (value?) in question. Also, the fileContents are now cached in the program so you don't have to reopen it every time you call the function.
When writing my VBA macros I often used "GoTo" so as to jump to a previous part of the macro without leaving the Sub. Now that I’m converting all my macros to Google Apps Script I’m trying to find the equivalent for “GoTo”.
Sub MySub()
Dim sheetname1 As String
Dim sheetname2 As String
On Error GoTo Err
sheetname1 = ActiveSheet.Name
Sheets.Add After:=Sheets(Sheets.Count)
ActiveSheet.Name = "passwords"
sheetname2 = ActiveSheet.Name
GoTo aftererr
Err:
MsgBox Error(Err)
Exit Sub
aftererr:
This is just one instance of my use of GoTo. However I need it for my new scripts in many other ways; not just for redirecting errors. For example:
function MyFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
if(criteraA == criteraB){
sh.offset(1,0).activate();
var i=i + 1;
//?? GoTo ??
}else{
var i=0;
sh.getRange(row, column)(1,sr.offset(0,1).getColumn()).activate();
}
You don't need GoTo, most people would argue that it is terrible programming practice to use it even when it is present. Using other control structures will do the job.
if() {
} else if() {
} else {
}
for(;;) {
continue;
break;
}
while() {
}
do {
} while();
switch() {
case:
default:
}
// for errors
throw "Error string"
try {
} catch(error) {
}
You'll have to shuffle your logic around a bit, but it will result is better more maintainable code.