i am trying to make a CLIish server in node.js.
but I need a way to parse a string and run a function from an object.
what I mean is... I don't want to nest a million switch statements just to have the commands I need.
using 2 other StackOverflow answers, I got 1 part done. inputs.
now i just need to figure out how to figure ou where the command stops and the input begins.
example:
inputting do say user:Yimmee msg:"well hello" "something random":yes
I need to separate do say and the inputs.
this is what i started with, but I do not know how to finish it.
function command(command, usable){
//usable is the object holding the commands that can be used.
//here I set commandMain to the part of command that is the command
/*and where commandInput is too. and I'm not forcing you,
but is preferably to be converted to an object.*/
var commandSplit = [];
do{
var match = (/[^ "]+|"([^"]*)"/gim).exec(commandMain);
if(match != null){
commandSplit.push(match[1] ? match[1] : match[0]);
}
}while (match != null);
var reach = `usable`;
commandSplit.forEach((to, nu)=>{
if(nu === commandSplit.length - 1){
reach += `["_${to}"]`;
}else{
reach += `["${to}"]`;
}
});
console.log(reach);
try{
return eval(reach)(commandInputs);
}catch(error){
return false;
}
}
Note I gave up a little, there will be some ridiculous errors.
big fat edit::::::::::::::::::::::L:::::::
idk how in the world process.argv works, and looking in one of the answers, i know how to set it.
but i am using a live websocket for this.
Unless this is an exercise, I'd strongly recommend not to implement your own command and argument parser. Use one of the existing libraries. A quick web search for "node cli library" yields a lot of results, including comparisons.
The libraries range from tiny and simple like minimist, very popular ones like yargs or commander, to heavier ones like oclif.
I'd also recommend checking the Command-line utilities section of Sindre Sorhus' Awesome Node.js list.
What you are doing is passing options and arguments to a program. You can use process.argv to get these.
It's always good to have useful error messages and command line documentation. Hence, if you're distributing to users, a more robust library for this purpose is worth an extra dependency. Widely used is yargs, see their website at https://www.npmjs.com/package/yargs for some examples.
If you want to do it using the basic process.argv, here's a solution:
This is your command in a format most people are used to: node some.js --user Yimmee --msg "well hello" --random
And the implementation
let arguments = process.argv.slice(2); // this removes `node` and the filename from arguments list
console.log(arguments)
switch (arguments[0]) { // check that `say` is the first "command"
case 'say':
let options = process.argv.slice(3); // get the stuff after `say`
let optionsObject = {} // key-value representation
if (options.indexOf("--user") != -1) { // if it exists
optionsObject.user = options[options.indexOf("--user")+1]
}
else {
// you can throw an error here
}
if (options.indexOf("--msg") != -1) { // if it exists
optionsObject.msg = options[options.indexOf("--msg")+1]
}
if (options.indexOf("--random") != -1) { // if it exists
optionsObject.random = true
}
console.log(optionsObject) // you can use optionsObject for your program
break;
default:
console.log("Invalid command");
}
EDIT: If this is happening inside the code as a function call, you can adapt above code:
function test(argsString) {
let arguments = argsString.split(/ (?=(?:(?:[^"]*"){2})*[^"]*$)/); // split the string into an array at the spaces
// ^ regex from https://stackoverflow.com/questions/23582276/
console.log(arguments)
switch (arguments[0]) { // check that `say` is the first "command"
case 'say':
let options = arguments.slice(1); // get the stuff after `say`
let optionsObject = {} // key-value representation
if (options.indexOf("--user") != -1) { // if it exists
optionsObject.user = options[options.indexOf("--user") + 1]
}
else {
// you can throw an error here
}
if (options.indexOf("--msg") != -1) { // if it exists
optionsObject.msg = options[options.indexOf("--msg") + 1]
}
if (options.indexOf("--random") != -1) { // if it exists
optionsObject.random = true
}
console.log(optionsObject) // you can use optionsObject for your program
break;
default:
console.log("Invalid command");
}
}
Related
I am struggling with doing an array search that includes a piece of text that must include back slashes in it. I have tried includes(''); negating includes(''); and also trying similar using indexOf('').
I have a simple array, with at maximum four values; typically it has two, here is how it typically looks:
{tks: Array(2)}
tks: Array(2)
0: "https://mycoolwebsite.net/arcgis/rest/services"
1: "https://mycoolwebsite.com/arcgis/rest/services"
length: 2
__proto__: Array(0)
__proto__: Object
Here are the simple checks I'm trying to do: My second check with *includes* 'wdt' text seems to be working so I assume it's something with the backslashes. Anyway I can handle this? I'm perplexed why my if and else both get hit with the first check below using back slashes... I added the negating on the else to double check.. with and without that in the else, else is always hit.
// right before doing the search I am building the array, just to add more context
for (let i = 0; i < coolObj.length; i++) {
if (coolObj[i].url){
tr = coolObj[i].url;
tks.push(tr);
console.log({tks});
}
}
console.log({tks}); // consoles as I have included above ^
if (tks.includes('/arcgis/rest/services')) {
console.log({tks});
} else if (!tks.includes('/arcgis/rest/services')) {
console.log({tks});
console.log('does not include correct url');
aT = '/arcgis/rest/services';
lP = false;
filterAt(aT);
}
if (tks.includes('wdt')) {
console.log({tks});
}else {
console.log({tks});
wT = 'wdt';
filterWt(wT);
}
From the MDN docs: The includes() method determines whether an array includes a certain value among its entries, returning true or false as appropriate.
You have to test the strings using String.prototype.includes within the array elements thus:
const arr = ["https://mycoolwebsite.net/arcgis/rest/services", "https://mycoolwebsite.com/arcgis/rest/services"];
arr.forEach(el => {
if (el.includes('/arcgis/rest/services')) {
console.log(el, ': includes url');
}
else {
console.log('does not include correct url');
}
if (el.includes('wdt')) {
console.log(el, ': includes wdt');
} else {
console.log('does not include correct wdt');
}
});
PS: I have already searched the forums and have seen the relevant posts for this wherein the same post exists but I am not able to resolve my issue with those solutions.
I have 2 json objects
var json1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222", addrs:"def", tab:"tab2"}];
var json2 = [{id:"tab1"},{id:"new"}];
I want to compare both these and check if the id element in json2 is present in json1 by comparing to its tab key. If not then set some boolean to false. ie by comparing id:"tab1" in json2 to tab:"tab1 in json1 .
I tried using below solutions as suggested by various posts:
var o1 = json1;
var o2 = json2;
var set= false;
for (var p in o1) {
if (o1.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
for (var p in o2) {
if (o2.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
Also tried with underscore as:
_.each(json1, function(one) {
_.each(json2, function(two) {
if (one.tab!== two.id) {
set= true;
}
});
});
Both of them fail for some test case or other.
Can anyone tell any other better method or outline the issues above.
Don't call them JSON because they are JavaScript arrays. Read What is JSON.
To solve the problem, you may loop over second array and then in the iteration check if none of the objects in the first array matched the criteria. If so, set the result to true.
const obj1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222",addrs:"def", tab:"tab2"}];
const obj2 = [{id:"tab1"},{id:"new"}];
let result = false;
for (let {id} of obj2) {
if (!obj1.some(i => i.tab === id)) {
result = true;
break;
}
}
console.log(result);
Unfortunately, searching the forums and reading the relevant posts is not going to replace THINKING. Step away from your computer, and write down, on a piece of paper, exactly what the problem is and how you plan to solve it. For example:
Calculate for each object in an array whether some object in another array has a tab property whose value is the same as the first object's id property.
There are many ways to do this. The first way involves using array functions like map (corresponding to the "calculate for each" in the question, and some (corresponding to the "some" in the question). To make it easier, and try to avoid confusing ourselves, we'll do it step by step.
function calculateMatch(obj2) {
return obj2.map(doesSomeElementInObj1Match);
}
That's it. Your program is finished. You don't even need to test it, because it's obviously right.
But wait. How are you supposed to know about these array functions like map and some? By reading the documentation. No one help you with that. You have to do it yourself. You have to do it in advance as part of your learning process. You can't do it at the moment you need it, because you won't know what you don't know!
If it's easier for you to understand, and you're just getting started with functions, you may want to write this as
obj2.map(obj1Element => doesSomeElementInObj1Match(obj1Element))
or, if you're still not up to speed on arrow functions, then
obj2.map(function(obj1Element) { return doesSomeElementInObj1Match(obj1Element); })
The only thing left to do is to write doesSomeElementInObj2Match. For testing purposes, we can make one that always returns true:
function doesSomeElementInObj2Match() { return true; }
But eventually we will have to write it. Remember the part of our English description of the problem that's relevant here:
some object in another array has a tab property whose value is the same as the first object's id property.
When working with JS arrays, for "some" we have the some function. So, following the same top-down approach, we are going to write (assuming we know what the ID is):
In the same way as above, we can write this as
function doesSomeElementInObj2Match(id) {
obj2.some(obj2Element => tabFieldMatches(obj2Element, id))
}
or
obj2.some(function(obj2Element) { return tabFieldMatches(obj2Element, id); })
Here, tabFieldMatches is nothing more than checking to make sure obj2Element.tab and id are identical.
We're almost done! but we still have to write hasMatchingTabField. That's quite easy, it turns out:
function hasMatchingTabField(e2, id) { return e2.tab === id; }
In the following, to save space, we will write e1 for obj1Element and e2 for obj2Element, and stick with the arrow functions. This completes our first solution. We have
const tabFieldMatches = (tab, id) { return tab === id; }
const hasMatchingTabField = (obj, id) => obj.some(e => tabFieldMatches(e.tab, id);
const findMatches = obj => obj.some(e => hasMatchingTabField(e1, obj.id));
And we call this using findMatches(obj1).
Old-fashioned array
But perhaps all these maps and somes are a little too much for you at this point. What ever happened to good old-fashioned for-loops? Yes, we can write things this way, and some people might prefer that alternative.
top: for (e1 of obj1) {
for (e2 of (obj2) {
if (e1.id === e2.tab) {
console.log("found match");
break top;
}
}
console.log("didn't find match);
}
But some people are sure to complain about the non-standard use of break here. Or, we might want to end up with an array of boolean parallel to the input array. In that case, we have to be careful about remembering what matched, at what level.
const matched = [];
for (e1 of obj1) {
let match = false;
for (e2 of obj2) {
if (e1.id === e2.tab) match = true;
}
matched.push(match);
}
We can clean this up and optimize it bit, but that's the basic idea. Notice that we have to reset match each time through the loop over the first object.
I've been observing some strange behaviour in a stored procedure in Azure Cosmos DB using javascript API (https://learn.microsoft.com/en-us/azure/cosmos-db/programming#javascript-language-integrated-query-api)
Assuming there is a document in the database with the body.id = '---' the procedure below correctly return that document. However, if I comment the line 'return found ' and uncomment the lines with 'if (1==1)' then the stored procedure returns an empty result. I tried changing it to if (found){return found;} else{return false;}' - same empty output. Also, same happens if I write 'found = found && (1==1);' after 'let found = c.body.id != null && c.body.id ==t;'
Is this a Javascript bug or me doing something wrong?
// SAMPLE STORED PROCEDURE
function sample() {
__.filter(c=>{
//return true;
let t = "---";
let found = c.body.id != null && c.body.id ==t;
return found; <---
//if (1==1){
// return found;
//}
}
, {pageSize: -1},
(a, b, c)=>{
__.response.setBody(b);
return;
});
}
I reproduce your issue on my side.
The following JavaScript constructs do not get optimized for Azure
Cosmos DB indices:
Control flow (for example, if, for, while) Function calls
Based on the statement in the doc, if is not recommanded to use in predicateFunction.
You could just filter documents by return expression(e.g. x.isMetadata === true);
I'm trying to create a dictionary from a .txt file in the shape of a tree. On every line of the text file there's a word, I extract all those words in an array.
Now regarding the tree, Each node contains a letter, if it's the last letter of a word, it contains a definition, and each node have an array Children that contains letters from all others words starting the same way.
So I have nodes defined this way:
function Node(letter,definition,children) {
this.letter = letter,
this.definition = "",
this.children = []
};
I have an array Dictionary that will contain all the nodes. Every Node will be organized (so that we know 'a' is in Dictionary[0] and 'b' in Dictionary[1] and so on).
I defined some functions to help build the dictionary:
check if Dictionary contains the first letter of the word we have (c is the character, dictio is the dictionary array and ascii is the ascii-97 value of the character)
function checkChar(c,dictio,ascii){
if(dictio[ascii].letter == c ){
return true;
}
return false;
};
create a node with the given character
function createChar(c){
var noeud = {
letter: c,
def: '',
children: []
};
return noeud;
};
Add the character to the dictionary
function addChar(c,dictio,ascii){
dictio.children[ascii] = createChar(c);
};
And I'm having trouble on the biggest function: the main on that adds the word and calls all of these small functions I've written. Which I'm having trouble making.
I don't even know if what I'm doing is right or wrong, if anyone could point me to the right direction or suggest a method in javascript or php to do dictionary from a TXT file that would be great.
Ok...
so this is an example of txt file containing words
//words.txt
hello
world
foo
bar
word_dictionary.php for parsing txt file and has method for checking if a word exists in tree/dictionary
<?php
//word_dictionary.php
class Node{
private $letter;
private $definition = '';
private $children = array();
function __construct($letter){
$this->letter = $letter;
}
function hasChild($letter){
return array_key_exists($letter,$this->children);
}
function addChild($letter){
$this->children[$letter] = new Node($letter);
return $this->children[$letter];
}
function getChild($letter){
return $this->children[$letter];
}
function setDefinition($definition){
$this->definition = $definition;
}
function getDefinition(){
return $this->definition;
}
function hasDefinition(){
return (bool)$this->definition;
}
}
// method for getting a word definition from tree/dictionary.
// if word exists return definition, else return false
function getDefinition($word,$tree){
$node = $tree;
$length = strlen($word);
foreach(str_split($word) as $index => $letter){
if($node->hasChild($letter)){
$node = $node->getChild($letter);
}
else{ // word not exists
return false;
}
if(($index+1) == $length){ // means last letter in word
return ($node->hasDefinition()) ? $node->getDefinition() : false;
}
}
}
// Start build your tree/dictionary. This part is execute ONCE only for building tree.
$anchor = new Node('');
$handle = fopen('words.txt','r');
while(($word = fgets($handle))){
$word = rtrim($word);
$length = strlen($word);
$node = $anchor;
foreach(str_split($word) as $index => $letter){
if($node->hasChild($letter)){
$node = $node->getChild($letter);
}
else{
$node = $node->addChild($letter);
}
if(($index+1) == $length ){
//print 'definition for word: '.$word."\n";
$node->setDefinition('definition for world: '.$word);
}
}
}
//use this function when a user type a word that you want to check if exists and return the definition to user. this flow should be in AJAX request from client
print getDefinition('bar',$anchor)."\n";
hope it help a bit ;)
first of all, you're asking if you are going in the right direction. Well, i think you are. This may not be the best implementation of the year but still all the things you said are coherent with each other and it seems pretty solid.
I don't think giving you a direct solution to your question would be didactic since you're working with trees and it seems you don't have much experience with them.
But i can give you some hint and references. A very convenient way to implement your "biggest function :)" would be to use a recursive function which would call itself on each children.
I suggest you take a look a this wikipedia article. It shows example of trees that look a bit like yours and implement a full search algorithm that you could adapt to your needs without too much problem.
Hope the english wasn't that bad, and that it will help you
wondering if I can make my javascript more efficient.
I have a var JSP = "the jsp's name"
And I have statements in a javascript validation file:
if(( JSP == "test.html" ) || ( JSP == "test1.html") || ( JSP == "test2.html" )) then blah blah blah.
Is there a more efficient way to do this?
If you know that JSP contains a string, it's slightly more efficient to use === rather than ==. Also note that you don't need all those parens:
if (JSP === "test.html" || JSP === "test1.html" || JSP === "test2.html") {
// do something
}
You could also use a regular expression:
if (/^test[12]?\.html$/.test(JSP)) {
// do something
}
...but it depends what you mean by "efficient." The series of === will be very efficient at runtime.
Separately, you could use a switch:
switch (JSP) {
case "test.html":
case "test1.html":
case "test2.html":
// Do it
break;
}
...but I wouldn't call it more efficient.
I definitely would not put the options in an array, because searching through the array will not be efficient. But you can use a map:
var pages = {
"test.html": true,
"test1.html": true,
"test2.html": true
};
...and then this test:
if (pages[JSP] === true) {
// do something
}
...which results in a fairly efficient property lookup. That's only reasonable if you create the object once and reuse it.
(You might have people say "Or just use if (pages[JSP]) { ... }. But that fails if JSP happens to contain "toString" or "valueOf" or any of several other inherited properties blank objects get from Object.prototype. It's fine if you're certain it won't have any of those values, though.)
You could create an object with those keys:
var theThings = { "test.html": true, "test1.html": true, "test2.html": true };
if (theThings[JSP]) { /* whatever */ }
If there are only three or four, it might not be worth it, but if there are dozens it'd definitely be faster, especially if the test gets made several times.
edit — wow I'm crying a little inside here, guys. Property name lookups are going to be way faster than linear searches through an array.
var arr = ['test.html', 'test1.html', 'test2.html'];
if (arr.indexOf(JSP)) != -1) {
alert("found it!");
}
relevant docs here.
if( JSP in {"test.html":0, "test2.html":0, "test3.html":0} ) {
...
}
It doesn't get any closer to SQL's IN( 1, 2, 3) than this in javascript :-)
if (["test.html", "test1.html", "test2.html"].indexOf(JSP) > -1)
For browsers that don't support indexOf on arrays, MDC suggests short piece of code that adds missing functionality.
Probably not more efficient, but you got cleaner ways to do it. You could for instance use a switch-case like this:
switch(JSP) {
case 'test.html':
case 'test1.html':
case 'test2.html':
blablabla; break;
}
Or you could create an array out of the urls and see if your string is in the array like this
var arr = [
'test.html', 'test1.html',
'test2.html'
];
if(arr.indexOf(JSP) != -1) { blablabla; }
The last one will not work in all browsers.
A way of doing it in jQuery is to use the inArray method, e.g.:
if ($.inArray(JSP, ["test.html", "test1.html", "test2.html"]) > -1) {
// your code
}
The inArray method works in a similar manner to String.indexOf so -1 is returned if no match.
Use a regular expression?
if (/^test\d?\.html$/.test(JSP)) { ... }
I can't promise that will be more efficient though, just tidier code-wise.
Or if you're already using jQuery, you could use jQuery.inArray():
var names = ['test.html', 'test2.html', 'test3.html'];
if ($.inArray(JSP, names)) { ... }
Or with underscore.js
var names = ['test.html', 'test2.html', 'test3.html'];
if (_.indexOf(names, JSP) !== -1) { ... }