Get functions parameter names JS - javascript

I don't know how to get parameter names, for example:
function func(sep='\n') {
var args = Array.prototype.slice.call(arguments);
}
func("Arg1", "Argument2", "Something", sep="MySeparator")
// I want to get value of "sep" in arguments of function
// In this example it should set optional parameter "sep" to "MySeparator"
Anyone can help?

No, you can’t get name of arguments, if you want name/value pairs as arguments then try to pass it as an object like show below.
function func(args) {
args.arg1;
args.arg2;
}
func({ arg1: “some value”, arg2: “other value” });

I'm not sure if I understood the problem correctly, but seems like you want to implement something like this:
function func(separator = '\n', ...args) {
return args.join(separator);
}
console.log(func(' - MySeparator - ', 'Arg1', 'Arg2', 'Arg3'));
The first argument in func accepts a separator you want to use, while all other arguments will be passed as an array into the function where you can use join to glue them together with your separator.
UPD1: Why we can not accept spread argument first and then the separator at the end?
As stated in MDN:
A function's last parameter can be prefixed with ... which will cause all remaining (user supplied) arguments to be placed within a "standard" Javascript array. Only the last parameter can be a "rest parameter".
Short explanation for this case: we don't know if the developer passed the argument into separator or the rest arguments. That is why we consuming the known positional arguments to match them to their appropriate variable names and only then we can consume "everything that left".

Related

Invoking Javascript array methods without the use of parentheses [duplicate]

I'm not sure how to explain this, but when I run
console.log`1`
In google chrome, I get output like
console.log`1`
VM12380:2 ["1", raw: Array[1]]
Why is the backtick calling the log function, and why is it making a index of raw: Array[1]?
Question brought up in the JS room by Catgocat, but no answers made sense besides something about templating strings that didn't really fit why this is happening.
It is called Tagged Template in ES-6 more could be read about them Here, funny I found the link in the starred section of the very chat.
But the relevant part of the code is below (you can basically create a filtered sort).
function tag(strings, ...values) {
assert(strings[0] === 'a');
assert(strings[1] === 'b');
assert(values[0] === 42);
return 'whatever';
}
tag `a${ 42 }b` // "whatever"
Basically, its merely tagging the "1" with console.log function, as it would do with any other function. The tagging functions accept parsed values of template strings and the values separately upon which further tasks can be performed.
Babel transpiles the above code to
var _taggedTemplateLiteralLoose = function (strings, raw) { strings.raw = raw; return strings; };
console.log(_taggedTemplateLiteralLoose(["1"], ["1"]));
As you can see it in the example above, after being transpiled by babel, the tagging function (console.log) is being passed the return value of the following es6->5 transpiled code.
_taggedTemplateLiteralLoose( ["1"], ["1"] );
The return value of this function is passed to console.log which will then print the array.
Tagged template literal:
The following syntax:
function`your template ${foo}`;
Is called the tagged template literal.
The function which is called as a tagged template literal receives the its arguments in the following manner:
function taggedTemplate(strings, arg1, arg2, arg3, arg4) {
console.log(strings);
console.log(arg1, arg2, arg3, arg4);
}
taggedTemplate`a${1}b${2}c${3}`;
The first argument is an array of all the individual string characters
The remaining argument correspond with the values of the variables which we receive via string interpolation. Notice in the example that there is no value for arg4 (because there are only 3 times string interpolation) and thus undefined is logged when we try to log arg4
Using the rest parameter syntax:
If we don't know beforehand how many times string interpolation will take place in the template string it is often useful to use the rest parameter syntax. This syntax stores the remaining arguments which the function receives into an array. For example:
function taggedTemplate(strings, ...rest) {
console.log(rest);
}
taggedTemplate `a${1}b${2}c${3}`;
taggedTemplate `a${1}b${2}c${3}d${4}`;
Late to the party but, TBH, none of the answers give an explanation to 50% of the original question ("why the raw: Array[1]")
1. Why is it possible to call the function without parenthesis, using backticks?
console.log`1`
As others have pointed out, this is called Tagged Template (more details also here).
Using this syntax, the function will receive the following arguments:
First argument: an array containing the different parts of the string that are not expressions.
Rest of arguments: each of the values that are being interpolated (ie. those which are expressions).
Basically, the following are 'almost' equivalent:
// Tagged Template
fn`My uncle ${uncleName} is ${uncleAge} years old!`
// function call
fn(["My uncle ", " is ", " years old!"], uncleName, uncleAge);
(see point 2. to understand why they're not exactly the same)
2. Why the ["1", raw: Array[1]] ???
The array being passed as the first argument contains a property raw, wich allows accessing the raw strings as they were entered (without processing escape sequences).
Example use case:
let fileName = "asdf";
fn`In the folder C:\Documents\Foo, create a new file ${fileName}`
function fn(a, ...rest) {
console.log(a); //In the folder C:DocumentsFoo, create a new file
console.log(a.raw); //In the folder C:\Documents\Foo, create a new file
}
What, an array with a property ??? ???
Yes, since JavaScript arrays are actually objects, they can store properties.
Example:
const arr = [1, 2, 3];
arr.property = "value";
console.log(arr); //[1, 2, 3, property: "value"]

Regex to define the number of appearances substituted [duplicate]

I'd like to know how to replace a capture group with its uppercase in JavaScript. Here's a simplified version of what I've tried so far that's not working:
> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'
Would you explain what's wrong with this code?
You can pass a function to replace.
var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });
Explanation
a.replace( /(f)/, "$1".toUpperCase())
In this example you pass a string to the replace function. Since you are using the special replace syntax ($N grabs the Nth capture) you are simply giving the same value. The toUpperCase is actually deceiving because you are only making the replace string upper case (Which is somewhat pointless because the $ and one 1 characters have no upper case so the return value will still be "$1").
a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))
Believe it or not the semantics of this expression are exactly the same.
I know I'm late to the party but here is a shorter method that is more along the lines of your initial attempts.
a.replace('f', String.call.bind(a.toUpperCase));
So where did you go wrong and what is this new voodoo?
Problem 1
As stated before, you were attempting to pass the results of a called method as the second parameter of String.prototype.replace(), when instead you ought to be passing a reference to a function
Solution 1
That's easy enough to solve. Simply removing the parameters and parentheses will give us a reference rather than executing the function.
a.replace('f', String.prototype.toUpperCase.apply)
Problem 2
If you attempt to run the code now you will get an error stating that undefined is not a function and therefore cannot be called. This is because String.prototype.toUpperCase.apply is actually a reference to Function.prototype.apply() via JavaScript's prototypical inheritance. So what we are actually doing looks more like this
a.replace('f', Function.prototype.apply)
Which is obviously not what we have intended. How does it know to run Function.prototype.apply() on String.prototype.toUpperCase()?
Solution 2
Using Function.prototype.bind() we can create a copy of Function.prototype.call with its context specifically set to String.prototype.toUpperCase. We now have the following
a.replace('f', Function.prototype.apply.bind(String.prototype.toUpperCase))
Problem 3
The last issue is that String.prototype.replace() will pass several arguments to its replacement function. However, Function.prototype.apply() expects the second parameter to be an array but instead gets either a string or number (depending on if you use capture groups or not). This would cause an invalid argument list error.
Solution 3
Luckily, we can simply substitute in Function.prototype.call() (which accepts any number of arguments, none of which have type restrictions) for Function.prototype.apply(). We have now arrived at working code!
a.replace(/f/, Function.prototype.call.bind(String.prototype.toUpperCase))
Shedding bytes!
Nobody wants to type prototype a bunch of times. Instead we'll leverage the fact that we have objects that reference the same methods via inheritance. The String constructor, being a function, inherits from Function's prototype. This means that we can substitute in String.call for Function.prototype.call (actually we can use Date.call to save even more bytes but that's less semantic).
We can also leverage our variable 'a' since it's prototype includes a reference to String.prototype.toUpperCase we can swap that out with a.toUpperCase. It is the combination of the 3 solutions above and these byte saving measures that is how we get the code at the top of this post.
Why don't we just look up the definition?
If we write:
a.replace(/(f)/, x => x.toUpperCase())
we might as well just say:
a.replace('f','F')
Worse, I suspect nobody realises that their examples have been working only because they were capturing the whole regex with parentheses. If you look at the definition, the first parameter passed to the replacer function is actually the whole matched pattern and not the pattern you captured with parentheses:
function replacer(match, p1, p2, p3, offset, string)
If you want to use the arrow function notation:
a.replace(/xxx(yyy)zzz/, (match, p1) => p1.toUpperCase()
Old post but it worth to extend #ChaosPandion answer for other use cases with more restricted RegEx. E.g. ensure the (f) or capturing group surround with a specific format /z(f)oo/:
> a="foobazfoobar"
'foobazfoobar'
> a.replace(/z(f)oo/, function($0,$1) {return $0.replace($1, $1.toUpperCase());})
'foobazFoobar'
// Improve the RegEx so `(f)` will only get replaced when it begins with a dot or new line, etc.
I just want to highlight the two parameters of function makes finding a specific format and replacing a capturing group within the format possible.
SOLUTION
a.replace(/(f)/,(m,g)=>g.toUpperCase())
for replace all grup occurrences use /(f)/g regexp. The problem in your code: String.prototype.toUpperCase.apply("$1") and "$1".toUpperCase() gives "$1" (try in console by yourself) - so it not change anything and in fact you call twice a.replace( /(f)/, "$1") (which also change nothing).
let a= "foobar";
let b= a.replace(/(f)/,(m,g)=>g.toUpperCase());
let c= a.replace(/(o)/g,(m,g)=>g.toUpperCase());
console.log("/(f)/ ", b);
console.log("/(o)/g", c);
Given a dictionary (object, in this case, a Map) of property, values, and using .bind() as described at answers
const regex = /([A-z0-9]+)/;
const dictionary = new Map([["hello", 123]]);
let str = "hello";
str = str.replace(regex, dictionary.get.bind(dictionary));
console.log(str);
Using a JavaScript plain object and with a function defined to get return matched property value of the object, or original string if no match is found
const regex = /([A-z0-9]+)/;
const dictionary = {
"hello": 123,
[Symbol("dictionary")](prop) {
return this[prop] || prop
}
};
let str = "hello";
str = str.replace(regex, dictionary[Object.getOwnPropertySymbols(dictionary)[0]].bind(dictionary));
console.log(str);
In the case of string conversion from CamelCase to bash_case (ie: for filenames), use a callback with ternary operator.
The captured group selected with a regexp () in the first (left) replace arg is sent to the second (right) arg that is a callback function.
x and y give the captured string (don't know why 2 times!) and index (the third one) gives the index of the beginning of the captured group in the reference string.
Therefor a ternary operator can be used not to place _ at first occurence.
let str = 'MyStringName';
str = str.replace(/([^a-z0-9])/g, (x,y,index) => {
return index != 0 ? '_' + x.toLowerCase() : x.toLowerCase();
});
console.log(str);

javascript method to accept a string or an array of strings

I'm in the process of building a wrapper library for quite a large service so that the team that I'm working with has an easier time developing using this service.
Basically, one of the API calls is called "subscribe" so that the program subscribes to one or more items (in order to track its changes). The API call takes 1 argument. The documentation shows this:
(string, required) Name identifier
OR (string, required) ID identifier
OR (array, optional) A JSON array of Name or ID identifiers
I did figure out how to use optional parameters, but I can't figure out to make an "either x or y" type of method in javascript.
You could check the parameter and convert single value to an array and work with that array.
function fn(parameter) {
return (Array.isArray(parameter) ? parameter : [parameter]).map(a => 'item' + a);
}
console.log(fn(1));
console.log(fn('abc'));
console.log(fn([3, 4]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Simpler may be to make it a variadic function by using a "rest parameter". You could define two parameters, the first required and the rest parameter allowing zero or more.
function subscribe(item, ...items) {
// item is required
// items may be zero or more additional items
}
You don't actually need two parameters, except perhaps for documentation to more clearly show a required argument.
Then they can call it with individual arguments, or if they already have an array, they can use that using spread syntax.
function subscribe(item, ...items) {
console.log("Found: %s, Then: %s", item, items);
}
subscribe("one", "two", "three");
var my_items = ["one", "two", "three"];
subscribe(...my_items);
Same as in Java, you can write several prototypes of a method, each taking a different number of parameters. When called, the right method will be called by itself.
For example:
/* You must check if i is a String */
function method(i){
}
/* You must check if i is a String and j is an Array */
function method(i, j){
}
Now, concerning the type of the input, JavaScript doesn't check the type in the method prototype, unlike Java or C for example. Therefore, you need to control the type of your input inside the method.
You can use isArray() method, or/and TypeOf operator when controlling the type.
Just check the input parameter for the string type of Array instance like this:
function method(prm) {
if (prm instanceof Array) {
console.info("(array, optional) A JSON array of Name or ID identifiers");
return;
}
if(typeof(prm) === "string") {
console.info("Name identifier|ID identifier");
return;
}
console.info("Unknown argument");
};
method("id");
method("name");
method(["id", "name"]);
method(1);

Javascript Variable Reference

Something I don't understand, which I'm sure someone with any simple knowledge of Javascript will get;
How does the 'm' variable referenced in this replace function actually refer to the input from the str - I don't understand how it takes the str as m?
str = str.replace("whatevers",function(m){ return m.toUpperCase(); })
Many thanks in advance. Tyler.
Each function defines how any functions passed in are used. The documentation for String.prototype.replace() explains how it's used in the section on specifying a function as a parameter.
Somewhere in the implementation of replace, that function you're passing in is called with several arguments. The full example is:
function replacer(match, p1, p2, p3, offset, string) {
return "replacement_text";
}
In the context of string replacing, if you pass in a function as the second parameter like the way you're doing, the first argument of that function that you pass in (in your case 'm') will be anything that matches your initial first argument (in this case "whatevers"). Once it finds a match, that gets assigns to 'm', and then it will perform the toUpperCase function on that variable 'm'.

How to best pass optional function arguments that are nearly always the same

What's the best way to pass an optional argument that is nearly always the same? Case in point is Javascript's parseInt function; the second argument for radix is optional, but to avoid a string beginning with a zero being treated as octal, it is generally recognized good practice to always specify 10 as the second argument. Fair enough, so I'm wondering, how to best deal with this situation; specifically, should I create a convenience function, such as parseBaseTenInt(str), that just returns parseInt(str, 10)? I've inherited one file of code with about 10 calls to parseInt without the second argument, and creating the convenience function will subsequently allow me to do a simple replacement of parseInt with parseBaseTenInt. However I can conceive of drawbacks to the convenience function, particularly it not being used by other developers. Because it's just 1 file right now, and I will the primary developer moving forward, it wouldn't be too inconvenient to just add the second argument to all the parseInt function calls.
I'd go with your solution - wrap it in a suitably-named function, keep it's behaviour/calling convention consistent with the library function, and use the wrapper function.
We use a lot a functions that apply a default if the passed value is null.
// base defaults to 10
function ParseInteger(str,base)
{
if(base==null)
base=10;
return parseInt(str,base);
}
You can either check how many arguments have been passed using arguments.length inside the function.
Or you set default values for each variable by using the || operator:
function foo(bar, baz) {
bar = bar || "default";
baz = baz || 0;
return bar + ", " + baz;
}
foo(); // "default, 0"
foo("some string"); // "some string, 0"
foo("some string", 1); // "some string, 1"
Depending on the number of parameters you might want to Introduce Parameter Object along with your convenience function. Then you can set defaults for your parameter object and only set the properties that vary when you use the function.

Categories