read parameters from url-string and execute proper function - javascript

I create some kind of "routing system" for RestfulAPI in function runUrl - on input I put url which contains some parameters (ID values) and I want to find this url in routes array, execute function given for that route with this parameters, and return that result as runUrl result.
function runUrl(url) {
return projects('f505ecfb74','5e735f505c'); // hardcoded mockup
// Question: how this function should look like ?
}
let routes = [
['/cars', cars ],
['/companies/:companyId/cars/:carId/projects', projects ],
['/companies/:companyId/room/:roomId', rooms ],
//...
];
// list of funtions to execute for given url
function cars() { return ["car1","car2"]; }
function projects(companyId,carId) { return [`proj-${companyId}`,`proj-${carId}`]; }
function rooms(companyId,roomId) { return `room-${companyId}-room-${roomId}` }
// ... (more functions)
// TEST
console.log(runUrl('/companies/f505ecfb74/cars/5e735f505c/projects'));
So far I write below function - but I have headache and it doesn't work
function runUrl(url) {
let route = routes.find( r=> url.match(r[0]) );
if(route) {
return route[1](url.match(route[0]));
}
return null;
}
The parameters values are alpha-numeric strings, parameters names in routes array start with : and then are alpha-numeric strings too. The number of parameters is arbitrary.
How function runUrl should look like?

I don't think you can do it with the match method because it's not meant to be equal between two strings, but I think I found an elegant solution to your problem, maybe it will help you :
let routes = [
['/cars', cars ],
['/companies/:companyId/cars/:carId/projects', projects ],
['/companies/:companyId/room/:roomId', rooms ],
];
function runUrl(url) {
let arrUrl = url.split('/') // it will make an array with any value after /
let route = routes.find( r=> arrUrl.length === r[0].split('/').length ); // it supposed to be equal by length
if(route) {
let params = arrUrl.filter(p => p && p.match(/\d+/)) // it will cut only the ids (string with number)
return route[1](params);
}
return null;
}
// list of funtions to execute for given url
function cars() {
return ["car1","car2"];
}
function projects(array) {
return [`proj-${array[0]}`,`proj-${array[1]}`];
}
function rooms(array) {
return `company-${array[0]}-room-${array[1]}`;
}
// ... (more functions)
// TEST
console.log(runUrl('/cars'))
console.log(runUrl('/companies/f505ecfb74/cars/5e735f505c/projects'))
console.log(runUrl('/companies/f505ecfb74/room/5e735f505c'))

Here is my proposition, similar to tomer raitz idea but use regexp to detect parameters
function runUrl(url) {
let result = undefined;
let u = url.split("/");
routes.find( ([route,func]) => {
let r = route.split("/");
if(r.length==u.length && r.every( (el,i) => /^:/.test(el) || el===u[i] ) ){
let params = u.filter((el,i)=> /^:/.test(r[i]));
result = func.call(this,...params);
return true;
}
return false;
})
return result;
}
// -----------
// TEST
// -----------
let routes = [
['/cars', cars ],
['/companies/:companyId/cars/:carId/projects', projects ],
['/companies/:companyId/room/:roomId', rooms ],
//...
];
function cars() { return ["car1","car2"]; }
function projects(companyId,carId) { return [`proj-${companyId}`,`proj-${carId}`]; }
function rooms(companyId,roomId) { return `room-${companyId}-room-${roomId}`; }
// TEST
console.log(runUrl('/companies/f505ecfb74/cars/5e735f505c/projects'));
console.log(runUrl('/cars'))
console.log(runUrl('/companies/ABC123/room/DEF5678'))

Related

Returning length of array after push in app function

I have the following app function that stores data. Users can add new items and it returns the length of the data array.
I am trying to understand why returning the data in a function returns the data including the pushed items, but returning the data.length does not and neither does applying a filter. If I request the data length outside of the app function scope, it does return it including the pushed items.
In my example, getfn , app.data.length and app.data.filter returns the added items, but getcntfn and filterinitialfn does not. Why is this?
var app = (function () {
var data = ["initial"];
function add(x) { data.push(x) }
function getfn() { return data };
function getcntfn() { return data.length };
function filterinitialfn(filter) { return data.filter(x => x == filter) }
return {
add: function (x) { add(x) },
data:data,
getfn: getfn(),
getcntfn: getcntfn(),
filterinitialfn: function(filter) {filterinitialfn(filter)}
}
}());
app.add("added")
console.log("app.getfn", app.getfn) //["initial", "added"]
console.log("app.getcntfn", app.getcntfn) //1 ???
console.log("app.data.length", app.data.length) //2
console.log("app.filterinitialfn", app.filterinitialfn("added")) //[] ???
console.log("app.filterinitial=>", app.data.filter(x => x == "added")) //["added"]
because you are calling the function immediately when returning it so it can't be changed from the initial state, so change it to:
return {
add: function (x) { add(x) },
data:data,
getfn: getfn,
getcntfn: getcntfn,
filterinitialfn: filterinitialfn,
}
of course, when using then you should include parentheses.
For example, the result of getfn: getfn() is evaluated when being initialised, it wont' change the value when calling it.
You'll need to pass the function itself, without calling it so it's evaluated on the call:
getfn: getfn,
getcntfn: getcntfn,
...
Since all your functions have the same name as the return object key, we can use the shorthand to create the object:
var app = (function () {
var data = ["initial"];
function add(x) { data.push(x) }
function getfn() { return data };
function getcntfn() { return data.length };
function filterinitialfn(filterBy) { return data.filter(x => x == "added") }
function filterBy(filterBy) { return data.filter(x => x == filterBy); }
return {
add,
data,
getfn,
getcntfn,
filterinitialfn,
filterBy
}
}());
app.add("added")
console.log("app.getfn", app.getfn())
console.log("app.getcntfn", app.getcntfn())
console.log("app.data.length", app.data.length)
console.log("app.filterinitialfn", app.filterinitialfn())
console.log("app.filterinitial=>", app.data.filter(x => x == "added"))
console.log("app.filterBy", app.filterBy('initial'))

Map each array element into a different function

Is there a way to directly map each element in an array into a separate function and return the result of each function into a separate variable for that element?
For example, I have this code:
arry = ["22-03-1995", 80.5, 1.83];
born = process_date(arry[0]); // call specific function for 1st element
weight = process_weight(arry[1]); // call specific function for 2nd element
height = process_height(array[2]); // call specific function for 3rd element
...
function process_date(d) { ... }
function process_weight(w) { ... }
function process_height(h) { ... }
or such an alternative method to achieve the same in a better shorter form.
if there's only one array you want to map then you probably want something like this:
const [born, weight, height] = [
process_date(arry[0]),
process_weight(arry[1]),
process_height(array[2])
]
if there are multiple arrays then that needs its own handling, you can create a function that takes the input array and returns the mapped array:
function mapArray(arr) {
return [
process_date(arr[0]),
process_weight(arr[1]),
process_height(arr[2])
]
}
arry.forEach(arr => {
const [born, weight, height] = mapArray(arr);
// do stuff with the variables here...
})
Check out this. Might help you somehow.
You can destruct your each arrays element and assign them to a new variable. Aswel you can pass array to function as arguments.
https://javascript.info/destructuring-assignment
You could put your functions into an object. Then put your values into an array of objects, so that you can have metadata to tell the value what function it should call.
Example
const valueObjects = [{
type: "date",
value: "22-03-1995"
}, {
type: "weight",
value: 80.5
}]
const calculations = {
date: function process_date(d) {...},
weight: function process_weight(w) {...}
};
valueObjects.forEach(valueObject => {
const processType = calculations[valueObject.type];
processType(valueObject.value);
})
Hope this can help you
arry = ["22-03-1995", 80.5, 1.83];
arrayFunc = [function process_date(d) { ... }, function process_weight(w) { ... }, function process_height(h) { ... } ]
array.forEach(myFunction);
let results = []
function myFunction(item, index) {
results << arrayFunc[index](item)
}
let born, weight, height;
[born, weight, height] = results;
console.log(born);
console.log(weight);
console.log(height);

How do I incorporate an options object as a parameter when I want to support an infinite amount of arguments?

I am accepting an infinite amount of arguments and looping through whatever the user provides. I would like to incorporate an options object into this module, but I am not sure how that would work. Would this code just see the options object as one of the normal params?
module.exports = function () {
var args = Array.prototype.slice.call(arguments);
var masterObject = [];
args.forEach(function (array) {
array.map(function (item) {
var found = false;
for (var i = 0; i < masterObject.length; i++) {
if (masterObject[i].word === item.word) {
masterObject[i].count += item.count;
found = true;
}
}
if (!found) {
masterObject.push(item);
}
});
});
return masterObject;
};
Well, you need to decide between
var mymodule = require('mymodule')(opts);
mymodule(1, 2, 3);
and
var mymodule = require('mymodule');
mymodule(1, 2, 3, opts);
or something else. The first one:
module.exports = function (opts) {
return function() {
var args = Array.prototype.slice.call(arguments);
// ...
};
};
The second one:
module.exports = function (opts) {
var args = Array.prototype.slice.call(arguments);
opts = args.pop();
// ...
};
I like Amadan's response to this problem, I just wanted to add a related note:
What I see more often than not: APIs that accept an infinite number of values, AND accept options, will use the combination of an array (to contain the n-length values) and an object to pass in the options.
This way you don't need any voodoo, and your API is super clean:
module.exports = function (array, opts) {
// parse your options
opts = opts || {};
// loop over array
array.forEach(item => {
// to infinity, and beyond!
});
}
If you want to go crazy, and can use ES6 Rest Parameters, but if you did that, the options parameter would have to be supplied first (and if the user didn't have any options to pass, they would have to use null or an empty object, which could be weird for some APIs, but it's a possible way to approach this problem):
module.exports = function (opts, ...args) {
// do something with your options
opts.foo = opts.foo || true;
// now loop through your infinite args
args.forEach(item => {
console.log(item);
});
}

javascript get only asked params inside function as arguments

I know we can get all arguments in javascript inside function when it is called anywhere . we can get extra arguments which we didnt asked also .
But can we get only asked arguments on javascript function?
Like :
function a(a,b){
console.log(arguments);
}
if we call function a somewhere a(1,2,3,4,5)
then the output will be like [1,2,3,4,5]
but i want only [1,2] as i have expected only two params in function?
My condition is
index: (req, res, next) => {
var params = ['batch_id', 'section_id', 'subject_id', 'term', 'assesment_id', 'assesment_type'];
var _ = req._;
req.utils.requestValidation([req,res,next], params,'query')
// But i dont want to send params like above always instead like below
req.utils.requestValidation(arguments, params,'query')
and where it is called is
requestValidation: (data, options, param) => {
if (!options) return;
var _ = data[0]._ || data._;
var rules = {};
var data = {};
var sanity = {};
var elr = [validator.escape, validator.ltrim, validator.rtrim];
options.map((item, index) => {
rules[item] = 'required';
sanity[item] = elr;
});
data[param] = sanity;
if (typeof data != 'string') {
sanitizer.setOptions(data);
var data = sanitizer.sanitize(data[0], data[1], data[2],param);
return data[0].validation.validate(rules, data[0][param]);
}
return data.validation.validate(rules, data[param]);
},
if you need only two parameters just cut arguments to two items
if you want automatic this you can write function-wrapper, something like this:
function wrapperCutParams(func, paramsCount){
return function(){
var args = Array.prototype.slice(arguments, 0);
if(args.length > paramsCount){
args = args.slice(0, paramsCount)
}
func.apply(this, args)
}
}
Then
var a = function a(a,b){
console.log(arguments);
}
a = wrapperCutParams(a, 2)
Or just
a = wrapperCutParams(function a(a,b){
console.log(arguments);
}, 2)
Since you declared those arguments the most readable way would be to use them as they are, if you need to put them in an array, just do it.
myArgs = [a, b];
Write a higher-order function which takes the underlying function as a parameter, and returns a function which truncates the argument list to the number of arguments the function is asking for, based on its length property.
function slice_args(fn) {
return function() {
return fn.apply(this, Array.prototype.slice.call(arguments, 0, fn.length));
};
}
Then
function a(a,b){
console.log(arguments);
}
var b = slice_args(a);
b(1,2,3,4,5)
> [1, 2]

Create anonymous function dynamically?

I'm working on simple evolutionary AI. I need to generate an anonymous function dynamically. For it I have a list of conditions and actions:
var conditions = [
function () { return enemyNear(), },
function () { return mySpeed() > 5; },
function () { return 1 === 1; }];
var actions = [
function () { return alert('walk'); },
function () { return alert('jump'); }
function () { return alert('attack'); } ]
The code chooses one of each to generate a new function:
condition = conditions [Math.floor(Math.random()*conditions .length)];
actions = conditions [Math.floor(Math.random()*actions .length)];
Provided it the chosen condition is enemyNear() and the chosen action is walk(), how can I generate the simple anonymous function?
behavior = function() {
if(enemyNear()) {
walk();
}
}
I can change the way the arrays are saved if needed. How can this be done?
All characters behaviors are called inside a loop like this:
for(i=0,i<chars.length,i++) {
chars[i].behavior.call();
}
The simplest way would be to only put functions inside the arrays:
var conditions = [
enemyNear,
function () { return mySpeed() > 5; },
function () { return 1 === 1;
}];
var actions = [walk, attack, jump];
Then you could define behave as something like:
var behave = function(condition, action) {
if(condition()) {
action();
}
}
And use it like for example:
behave(conditions[2], actions[1]);
Here, 2 and 1 could be a randomly generated number like this:
var getRandomInt = function (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var actRandomly = function (conditions, actions) {
behave(
conditions[getRandomInt(0, conditions.length -1)],
actions[getRandomInt(0, actions.length -1)]
);
};
Call it like:
actRandomly(conditions, actions);
Of course this only presents the idea, and is not neatly organized.
For the fun of it, I created a Basic jsFiddle Demo Version.
Note: Always use var when you are defining a variable. Don't pollute the global scope.
First, inside of the arrays you will need to have a reference to the condition and action function. Right now, you are calling them, so they basically are the same as a useless array like:
conditions = [true, false, true];
actions = [undefined, undefined, undefined];
I would correct it to something like this:
var conditions = [enemyNear,
function() { return mySpeed() > 5 },
function(){ return true; }];
var actions = [walk, attack, jump];
And then you can create a function that generates a behavior:
function generateBehavior(){
var condition = conditions[Math.floor(Math.random() * conditions.length)];
var action = actions[Math.floor(Math.random() * actions.length)];
return function() {
if(condition()) {
action();
}
}
}
You can see it in action on this JSFiddle demo.
Change your arrays from this:
conditions = [enemyNear(), mySpeed()>5, 1=1];
To this:
conditions = [
function() { return enemyNear() }, // or just enemyNear
function() { return mySpeed() > 5 },
function() { return 1 == 1 } // 1 = 1?
];
With your current code, your functions are being called and conditions becomes an array of the outputs of those functions.
condition = [
enemyNear,
function() { return mySpeed() > 5; },
function() { return 1 == 1; }
];
You need to store your functions in the array, not call them, otherwise you are storing the result of the functions in the array.
function enemyNear() {}
function walk() {}
conditions = [enemyNear]
actions = [walk]
behaviour = function() {
if(conditions[randomNumber]()) {
actions[randomNumber]();
}
}
Simple. Use eval()
var behavior;
eval("behavior = function() { if (enemyNear()) walk(); }");
behavior();

Categories