ES6 square brackets, comma separated values, as expression [duplicate] - javascript
I read this question about the "comma operator" in expressions (,) and the MDN docs about it, but I can't think of a scenario where it is useful.
So, when is the comma operator useful?
The following is probably not very useful as you don't write it yourself, but a minifier can shrink code using the comma operator. For example:
if(x){foo();return bar()}else{return 1}
would become:
return x?(foo(),bar()):1
The ? : operator can be used now, since the comma operator (to a certain extent) allows for two statements to be written as one statement.
This is useful in that it allows for some neat compression (39 -> 24 bytes here).
I'd like to stress the fact that the comma in var a, b is not the comma operator because it doesn't exist within an expression. The comma has a special meaning in var statements. a, b in an expression would be referring to the two variables and evaluate to b, which is not the case for var a, b.
The comma operator allows you to put multiple expressions in a place where one expression is expected. The resulting value of multiple expressions separate by a comma will be the value of the last comma separated expression.
I don't personally use it very often because there aren't that many situations where more than one expression is expected and there isn't a less confusing way to write the code than using the comma operator. One interesting possibility is at the end of a for loop when you want more than one variable to be incremented:
// j is initialized to some other value
// as the for loop executes both i and j are incremented
// because the comma operator allows two statements to be put in place of one
for (var i = 0; i < items.len; i++, j++) {
// loop code here that operates on items[i]
// and sometimes uses j to access a different array
}
Here you see that i++, j++ can be put in a place where one expression is allowed. In this particular case, the multiple expressions are used for side affects so it does not matter that the compound expressions takes on the value of the last one, but there are other cases where that might actually matter.
The Comma Operator is frequently useful when writing functional code in Javascript.
Consider this code I wrote for a SPA a while back which had something like the following
const actions = _.chain(options)
.pairs() // 1
.filter(selectActions) // 2
.map(createActionPromise) // 3
.reduce((state, pair) => (state[pair[0]] = pair[1], state), {}) // 4
.value();
This was a fairly complex, but real-world scenario. Bear with me while I explain what is happening, and in the process make the case for the Comma Operator.
This uses Underscore's chaining to
Take apart all of the options passed to this function using pairs
which will turn { a: 1, b: 2} into [['a', 1], ['b', 2]]
This array of property pairs is filtered by which ones are deemed to be 'actions' in the system.
Then the second index in the array is replaced with a function that returns a promise representing that action (using map)
Finally the call to reduce will merge each "property array" (['a', 1]) back into a final object.
The end result is a transformed version of the options argument, which contains only the appropriate keys and whose values are consumable by the calling function.
Looking at just
.reduce((state, pair) => (state[pair[0]] = pair[1], state), {})
You can see the reduce function starts with an empty state object, state, and for each pair representing a key and value, the function returns the same state object after adding a property to the object corresponding to the key/value pair. Because of ECMAScript 2015's arrow function syntax, the function body is an expression, and as a result, the Comma Operator allows a concise and useful "iteratee" function.
Personally I have come across numerous cases while writing Javascript in a more functional style with ECMAScript 2015 + Arrow Functions. Having said that, before encountering arrow functions (such as at the time of the writing of the question), I'd never used the comma operator in any deliberate way.
Another use for the comma operator is to hide results you don't care about in the repl or console, purely as a convenience.
For example, if you evaluate myVariable = aWholeLotOfText in the repl or console, it will print all the data you just assigned. This might be pages and pages, and if you'd prefer not to see it, you can instead evaluate myVariable = aWholeLotOfText, 'done', and the repl/console will just print 'done'.
Oriel correctly points out† that customized toString() or get() functions might even make this useful.
Comma operator is not specific to JavaScript, it is available in other languages like C and C++. As a binary operator this is useful when the first operand, which is generally an expression, has desired side effect required by second operand. One example from wikipedia:
i = a += 2, a + b;
Obviously you can write two different lines of codes, but using comma is another option and sometimes more readable.
I'd disagree with Flanagan, and say, that comma is really useful and allows to write more readable and elegant code, especially when you know what you're doing:
Here's the greatly detailed article on comma usage:
Several examples from out from there for the proof of demonstration:
function renderCurve() {
for(var a = 1, b = 10; a*b; a++, b--) {
console.log(new Array(a*b).join('*'));
}
}
A fibonacci generator:
for (
var i=2, r=[0,1];
i<15;
r.push(r[i-1] + r[i-2]), i++
);
// 0,1,1,2,3,5,8,13,21,34,55,89,144,233,377
Find first parent element, analogue of jQuery .parent() function:
function firstAncestor(el, tagName) {
while(el = el.parentNode, el && (el.tagName != tagName.toUpperCase()));
return el;
}
//element in http://ecma262-5.com/ELS5_HTML.htm
var a = $('Section_15.1.1.2');
firstAncestor(a, 'div'); //<div class="page">
I haven't found practical use of it other than that but here is one scenario in which James Padolsey nicely uses this technique for IE detection in a while loop:
var ie = (function(){
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while ( // <-- notice no while body here
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
);
return v > 4 ? v : undef;
}());
These two lines must to execute :
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
And inside comma operator, both are evaluated though one could have made them separate statements somehow.
There is something "odd" that can be done in JavaScript calling a function indirectly by using the comma operator.
There is a long description here:
Indirect function call in JavaScript
By using this syntax:
(function() {
"use strict";
var global = (function () { return this || (1,eval)("this"); })();
console.log('Global === window should be true: ', global === window);
var not_global = (function () { return this })();
console.log('not_global === window should be false: ', not_global === window);
}());
You can get access to the global variable because eval works differently when called directly vs called indirectly.
I've found the comma operator most useful when writing helpers like this.
const stopPropagation = event => (event.stopPropagation(), event);
const preventDefault = event => (event.preventDefault(), event);
const both = compose(stopPropagation, preventDefault);
You could replace the comma with either an || or &&, but then you'd need to know what the function returns.
More important than that, the comma separator communicates intent -- the code doesn't care what the left-operand evaluates to, whereas the alternatives may have another reason for being there. This in turn makes it easier to understand and refactor. If the function return type ever changes, the code above would not be affected.
Naturally you can achieve the same thing in other ways, but not as succinctly. If || and && found a place in common usage, so too can the comma operator.
One typical case I end up using it is during optional argument parsing. I think it makes it both more readable and more concise so that the argument parsing doesn't dominate the function body.
/**
* #param {string} [str]
* #param {object} [obj]
* #param {Date} [date]
*/
function f(str, obj, date) {
// handle optional arguments
if (typeof str !== "string") date = obj, obj = str, str = "default";
if (obj instanceof Date) date = obj, obj = {};
if (!(date instanceof Date)) date = new Date();
// ...
}
Let's say you have an array:
arr = [];
When you push onto that array, you are rarely interested in push's return value, namely the new length of the array, but rather the array itself:
arr.push('foo') // ['foo'] seems more interesting than 1
Using the comma operator, we can push onto the array, specify the array as the last operand to comma, and then use the result -- the array itself -- for a subsequent array method call, a sort of chaining:
(arr.push('bar'), arr.push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]
It saves you from using return in nested conditionals and it's very handy especially with the ternary operator. Such as;
function insert(v){
return this.node > v ? this.left.size < this.right.size ? ( this.left.insert(v)
, this
)
: ( this.left.insert(this.node)
, this.node = this.right.popmin()
, this.insert(v)
, this
)
: this.left.size < this.right.size ? ( this.right.insert(this.node)
, this.node = this.left.popmax()
, this.insert(v)
, this
)
: ( this.right.insert(v)
, this
)
}
I just came across this today looking at the proposals for pipeline operator proposal and partial application...
(https://github.com/tc39/proposal-pipeline-operator
(https://github.com/tc39/proposal-partial-application#hack-style-pipelines)
Also, Hack-style pipelines are already feasible without introducing new syntax today:
let $; // Hack-style topic variable
let result = (
$= books,
$= filter($, _ => _.title = "..."),
$= map($, _ => _.author),
$);
The use of comma expressions here can kind of fake the pipeline operator that isn't in the language yet.
Eliminating the space between $= simulates the feeling of a proper pipe token, |>. Note that the "topic" variable, $, can be anything here and that it's just shorthand for repeatedly overwriting the variable. So something more akin to ...
// blocking inside an IIFE
let result = (() => {
let $;
$ = books;
$ = filter($, _ => _.title = "..."),
$ = map($, _ => _.author),
return $;
})()
The "comma" version successfully cuts out some of the noise, getting you closer to what the proposal would be:
let result = books
|> filter($, _ => _.title = "..."
|> map($, _ => _.author)
Here's another example using it to compose functions:
const double = (x) => 2 * x;
const add = (x, y) => x + y;
const boundScore = (min, max, score) => Math.max(min, Math.min(max, score));
const calculateScore = ($) => (
$= double($),
$= add($, 20),
$= boundScore(0, 100, $),
(console.log($), $)
)
const score = calculateScore(28)
The comma operator (,) evaluates each of its operands (from left to right) and returns the value of the last operand. This lets you create a compound expression in which multiple expressions are evaluated, with the compound expression's final value being the value of the rightmost of its member expressions. This is commonly used to provide multiple parameters to a for loop.
let x = 1;
x = (x++, x);
console.log(x);
// expected output: 2
x = (2, 3);
console.log(x);
// expected output: 3
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
Another area where comma operator can be used is Code Obfuscation.
Let's say a developper writes some code like this:
var foo = 'bar';
Now, she decides to obfuscate the code. The tool used may changed the code like this:
var Z0b=(45,87)>(195,3)?'bar':(54,65)>(1,0)?'':'baz';// Z0b == 'bar'
Demo: http://jsfiddle.net/uvDuE/
Related
Comma operator in JavaScript
Following this tutorial, what do these lines mean? var join = require('path').join , pfx = join(__dirname, '../_certs/pfx.p12'); The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand. How could these lines easier be written?
In this case, the comma separates two variables, and that's it, it's the same as writing var join = require('path').join; var pfx = join(__dirname, '../_certs/pfx.p12'); Instead one can do var join = require('path').join, pfx = join(__dirname, '../_certs/pfx.p12'); In this case, the comma is just a seperator, much as it would be an object literal or array. The comma operator, which is only an operator when it acts on two expressions, one on the left side, and one on the right side, can be used when you want to include multiple expressions in a location that requires a single expression. One example would be in a return statement [1,2,3].reduce(function(a,b,i) { return a[i] = b, a; // returns a; },[]); etc...
It's essentially the same as a semi-colon in many cases, so you could rewrite it as this: var join = require('path').join; var pfx = join(__dirname, '../_certs/pfx.p12'); The difference comes down to lines like declaring variables (like your example), in which the var is applied to each element in the comma-separated list. Beyond that, it's more or less a semi-colon, though it's not recommended to use the comma syntax in most cases. I personally prefer it for the variables, because I think this look a little cleaner: var a = 5, b = 6, c, d; But others dislike it.
Can you concatenate an if statement without using a ternary?
I'd like to concatenate an if statement like so: policeRecord = "Hi" + (if (x < crimes.length) {", ";}); I know that you can do it using a ternary: policeRecord = "Hi" + ((x < crimes.length) ? ", " : ""); But can you do it with a full if statement?
if is a statement. Statements cannot be used as part of an expression. Typically expressions consist of operators and sub-expressions and create values. Statements don't belong in any of these categories, they are not expressions and not operators. For more technical information, have a look at the ECMAScript specification: http://es5.github.com/.
No, that is invalid JavaScript syntax so you cannot do it. I'm not sure why you would want to do it either because I actually thing it causes a loss of clarity (there's a lot of jumble of symbols there). If you wanted to do some sort of fancy one-liner to confuse people without using the ternary operator, you could do array/string manipulation based on element length: policeRecord = "Hi" + (new Array(+(x < crimes.length) + 1)).join(', ');
if is a statement and doesn't return a value, whereas ? is an operator. In the case of function it's notable, that there is a function statement and a function operator, so you can do function () { } // function statement and you can also do var fn = function () { }; // function operator Short answer: You can't replace or interchange if and ?. You may create a function that does it, but running through a function has not the performance of native expressions.
You can also create a function à la SQL's COALESCE (but checking for true instead of NULL): function iftrue(checkThis, returnIfTrue, returnIfFalse) { if(checkThis) { return returnIfTrue; } return returnIfFalse; } policeRecord = "Hi" + iftrue(1 < crimes.length, ", ", ""); Not sure how much better that would be, though.
When is the comma operator useful?
I read this question about the "comma operator" in expressions (,) and the MDN docs about it, but I can't think of a scenario where it is useful. So, when is the comma operator useful?
The following is probably not very useful as you don't write it yourself, but a minifier can shrink code using the comma operator. For example: if(x){foo();return bar()}else{return 1} would become: return x?(foo(),bar()):1 The ? : operator can be used now, since the comma operator (to a certain extent) allows for two statements to be written as one statement. This is useful in that it allows for some neat compression (39 -> 24 bytes here). I'd like to stress the fact that the comma in var a, b is not the comma operator because it doesn't exist within an expression. The comma has a special meaning in var statements. a, b in an expression would be referring to the two variables and evaluate to b, which is not the case for var a, b.
The comma operator allows you to put multiple expressions in a place where one expression is expected. The resulting value of multiple expressions separate by a comma will be the value of the last comma separated expression. I don't personally use it very often because there aren't that many situations where more than one expression is expected and there isn't a less confusing way to write the code than using the comma operator. One interesting possibility is at the end of a for loop when you want more than one variable to be incremented: // j is initialized to some other value // as the for loop executes both i and j are incremented // because the comma operator allows two statements to be put in place of one for (var i = 0; i < items.len; i++, j++) { // loop code here that operates on items[i] // and sometimes uses j to access a different array } Here you see that i++, j++ can be put in a place where one expression is allowed. In this particular case, the multiple expressions are used for side affects so it does not matter that the compound expressions takes on the value of the last one, but there are other cases where that might actually matter.
The Comma Operator is frequently useful when writing functional code in Javascript. Consider this code I wrote for a SPA a while back which had something like the following const actions = _.chain(options) .pairs() // 1 .filter(selectActions) // 2 .map(createActionPromise) // 3 .reduce((state, pair) => (state[pair[0]] = pair[1], state), {}) // 4 .value(); This was a fairly complex, but real-world scenario. Bear with me while I explain what is happening, and in the process make the case for the Comma Operator. This uses Underscore's chaining to Take apart all of the options passed to this function using pairs which will turn { a: 1, b: 2} into [['a', 1], ['b', 2]] This array of property pairs is filtered by which ones are deemed to be 'actions' in the system. Then the second index in the array is replaced with a function that returns a promise representing that action (using map) Finally the call to reduce will merge each "property array" (['a', 1]) back into a final object. The end result is a transformed version of the options argument, which contains only the appropriate keys and whose values are consumable by the calling function. Looking at just .reduce((state, pair) => (state[pair[0]] = pair[1], state), {}) You can see the reduce function starts with an empty state object, state, and for each pair representing a key and value, the function returns the same state object after adding a property to the object corresponding to the key/value pair. Because of ECMAScript 2015's arrow function syntax, the function body is an expression, and as a result, the Comma Operator allows a concise and useful "iteratee" function. Personally I have come across numerous cases while writing Javascript in a more functional style with ECMAScript 2015 + Arrow Functions. Having said that, before encountering arrow functions (such as at the time of the writing of the question), I'd never used the comma operator in any deliberate way.
Another use for the comma operator is to hide results you don't care about in the repl or console, purely as a convenience. For example, if you evaluate myVariable = aWholeLotOfText in the repl or console, it will print all the data you just assigned. This might be pages and pages, and if you'd prefer not to see it, you can instead evaluate myVariable = aWholeLotOfText, 'done', and the repl/console will just print 'done'. Oriel correctly points out† that customized toString() or get() functions might even make this useful.
Comma operator is not specific to JavaScript, it is available in other languages like C and C++. As a binary operator this is useful when the first operand, which is generally an expression, has desired side effect required by second operand. One example from wikipedia: i = a += 2, a + b; Obviously you can write two different lines of codes, but using comma is another option and sometimes more readable.
I'd disagree with Flanagan, and say, that comma is really useful and allows to write more readable and elegant code, especially when you know what you're doing: Here's the greatly detailed article on comma usage: Several examples from out from there for the proof of demonstration: function renderCurve() { for(var a = 1, b = 10; a*b; a++, b--) { console.log(new Array(a*b).join('*')); } } A fibonacci generator: for ( var i=2, r=[0,1]; i<15; r.push(r[i-1] + r[i-2]), i++ ); // 0,1,1,2,3,5,8,13,21,34,55,89,144,233,377 Find first parent element, analogue of jQuery .parent() function: function firstAncestor(el, tagName) { while(el = el.parentNode, el && (el.tagName != tagName.toUpperCase())); return el; } //element in http://ecma262-5.com/ELS5_HTML.htm var a = $('Section_15.1.1.2'); firstAncestor(a, 'div'); //<div class="page">
I haven't found practical use of it other than that but here is one scenario in which James Padolsey nicely uses this technique for IE detection in a while loop: var ie = (function(){ var undef, v = 3, div = document.createElement('div'), all = div.getElementsByTagName('i'); while ( // <-- notice no while body here div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', all[0] ); return v > 4 ? v : undef; }()); These two lines must to execute : div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', all[0] And inside comma operator, both are evaluated though one could have made them separate statements somehow.
There is something "odd" that can be done in JavaScript calling a function indirectly by using the comma operator. There is a long description here: Indirect function call in JavaScript By using this syntax: (function() { "use strict"; var global = (function () { return this || (1,eval)("this"); })(); console.log('Global === window should be true: ', global === window); var not_global = (function () { return this })(); console.log('not_global === window should be false: ', not_global === window); }()); You can get access to the global variable because eval works differently when called directly vs called indirectly.
I've found the comma operator most useful when writing helpers like this. const stopPropagation = event => (event.stopPropagation(), event); const preventDefault = event => (event.preventDefault(), event); const both = compose(stopPropagation, preventDefault); You could replace the comma with either an || or &&, but then you'd need to know what the function returns. More important than that, the comma separator communicates intent -- the code doesn't care what the left-operand evaluates to, whereas the alternatives may have another reason for being there. This in turn makes it easier to understand and refactor. If the function return type ever changes, the code above would not be affected. Naturally you can achieve the same thing in other ways, but not as succinctly. If || and && found a place in common usage, so too can the comma operator.
One typical case I end up using it is during optional argument parsing. I think it makes it both more readable and more concise so that the argument parsing doesn't dominate the function body. /** * #param {string} [str] * #param {object} [obj] * #param {Date} [date] */ function f(str, obj, date) { // handle optional arguments if (typeof str !== "string") date = obj, obj = str, str = "default"; if (obj instanceof Date) date = obj, obj = {}; if (!(date instanceof Date)) date = new Date(); // ... }
Let's say you have an array: arr = []; When you push onto that array, you are rarely interested in push's return value, namely the new length of the array, but rather the array itself: arr.push('foo') // ['foo'] seems more interesting than 1 Using the comma operator, we can push onto the array, specify the array as the last operand to comma, and then use the result -- the array itself -- for a subsequent array method call, a sort of chaining: (arr.push('bar'), arr.push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]
It saves you from using return in nested conditionals and it's very handy especially with the ternary operator. Such as; function insert(v){ return this.node > v ? this.left.size < this.right.size ? ( this.left.insert(v) , this ) : ( this.left.insert(this.node) , this.node = this.right.popmin() , this.insert(v) , this ) : this.left.size < this.right.size ? ( this.right.insert(this.node) , this.node = this.left.popmax() , this.insert(v) , this ) : ( this.right.insert(v) , this ) }
I just came across this today looking at the proposals for pipeline operator proposal and partial application... (https://github.com/tc39/proposal-pipeline-operator (https://github.com/tc39/proposal-partial-application#hack-style-pipelines) Also, Hack-style pipelines are already feasible without introducing new syntax today: let $; // Hack-style topic variable let result = ( $= books, $= filter($, _ => _.title = "..."), $= map($, _ => _.author), $); The use of comma expressions here can kind of fake the pipeline operator that isn't in the language yet. Eliminating the space between $= simulates the feeling of a proper pipe token, |>. Note that the "topic" variable, $, can be anything here and that it's just shorthand for repeatedly overwriting the variable. So something more akin to ... // blocking inside an IIFE let result = (() => { let $; $ = books; $ = filter($, _ => _.title = "..."), $ = map($, _ => _.author), return $; })() The "comma" version successfully cuts out some of the noise, getting you closer to what the proposal would be: let result = books |> filter($, _ => _.title = "..." |> map($, _ => _.author) Here's another example using it to compose functions: const double = (x) => 2 * x; const add = (x, y) => x + y; const boundScore = (min, max, score) => Math.max(min, Math.min(max, score)); const calculateScore = ($) => ( $= double($), $= add($, 20), $= boundScore(0, 100, $), (console.log($), $) ) const score = calculateScore(28)
The comma operator (,) evaluates each of its operands (from left to right) and returns the value of the last operand. This lets you create a compound expression in which multiple expressions are evaluated, with the compound expression's final value being the value of the rightmost of its member expressions. This is commonly used to provide multiple parameters to a for loop. let x = 1; x = (x++, x); console.log(x); // expected output: 2 x = (2, 3); console.log(x); // expected output: 3 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
Another area where comma operator can be used is Code Obfuscation. Let's say a developper writes some code like this: var foo = 'bar'; Now, she decides to obfuscate the code. The tool used may changed the code like this: var Z0b=(45,87)>(195,3)?'bar':(54,65)>(1,0)?'':'baz';// Z0b == 'bar' Demo: http://jsfiddle.net/uvDuE/
Replace a Regex capture group with uppercase in Javascript
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);
code-style: Is inline initialization of JS objects ok?
I often find myself using inline initialization (see example below), especially in a switch statement when I don't know which case loop will hit. I find it easier to read than if statements. But is this good practice or will it incur side-effects or a performance hit? for (var i in array) { var o = o ? o : {}; // init object if it doesn't exist o[array[i]] = 1; // add key-values } Is there a good website to go to get coding style tips?
Another commonly used pattern to do the same, is the use of the Logical OR || operator (a little bit more readable than your ternary IMHO): //... var obj = o || {}; This operator will return its second operand if the first one evaluates to false, otherwise it will return the first one. Is safe to use it when you expect an object, since those falsy values are null, undefined, NaN, 0, a zero-length string, and of course false. I find it useful to set default values on function arguments, when of course any of the falsy values are expected as valid by the function: function test (arg1) { arg1 = arg1 || "default value"; //.. }
Why not just declare it outside the loop? var o = {}; for (var i in array) { o[array[i]] = 1; } Otherwise no, I don't see a problem with what you're doing.