javascript JSON and Array elements, help me understand the rule about quotes - javascript

When using a returned value to determine the number of an element in an array, does javascript throw quotes around it?
Example :
This tallys the number of times unique characters are used.
var uniques = {};
function charFreq(s)
{
for(var i = 0; i < s.length; i++)
{
if(isNaN(uniques[s.charAt(i)])) uniques[s.charAt(i)] = 1;
else uniques[s.charAt(i)] = uniques[s.charAt(i)] + 1;
}
return uniques;
}
console.log(charFreq("ahasdsadhaeytyeyeyehahahdahsdhadhahhhhhhhhhha"));
It just seems funny that uniques[s.charAt(i)] works, and uniques[a] wont work (due to lack of quotes). uniques[a] will get you a nasty 'a is undefined'.

When you access a JavaScript object using the [] notation, you are using a string as a key in the object. You can also address properties using the . notation:
uniques.a is the same as uniques['a']
The reason you aren't adding quotes to the s.charAt(i) is that it returns a string, which is then used as the property to check on the uniques object.
uniques[a] will create an error, because no variable with the name a has been defined.

In the first version -- uniques[s.charAt(i)] -- you're doing the lookup using an expression. JavaScript evaluates the expression -- s.charAt(i) -- and uses the evaluated value (maybe a) to perform the lookup in the uniques map.
In the second version -- uniques[a] -- you want to do the lookup using the literal character a, but unless you wrap it in quotes then JavaScript treats the a as an expression rather than a literal. When it tries to evaluate the "expression" then you get an error.
So the rule is: character/string literals need quotes; expressions that evaluate to characters/strings don't.

This is how Javascript evaluates the expression between [] like uniques[s.charAt(i)] which is of the type MemberExpression[ Expression ] :
Let propertyNameReference be the result of evaluating Expression.
Let propertyNameValue be GetValue(propertyNameReference).
Let propertyNameString be ToString(propertyNameValue).
So in the 3rd step it is converting the property name into a string.

Related

Arguments before a colon

I'm trying to pass an argument to a function in Javascript. However, the argument column is being interpreted as a literal string as opposed to an argument to the function. This is due to the colon directly following the argument column:
update(column, selected) {
var cols = {...this.props.columns, column: selected}
this.props.dispatch(updateColumns({type: "UPDATE_COLUMNS", cols}))
}
I've tried using arguments[0] instead of column but then I am told arguments is a reserved word in strict mode. Any idea how I can get read this value as an argument as opposed to a string?
In modern JavaScript environments, object initializer syntax allows for expressions to be evaluated on the left side of the property initialization:
update(column, selected) {
var cols = {...this.props.columns, [column]: selected}
this.props.dispatch(updateColumns({type: "UPDATE_COLUMNS", cols}))
}
The square brackets mean that the expression inside should be evaluated, and the result should be used as the property name.
In older environments, you'd have to do it in a separate statement, but since you're already using spread syntax I'd guess you're not concerned with that.

Javascript: access an object property whose name starts with a number

I'm creating a Javascript / jQuery application.
I need to process a JSON response that represents a HashMap, like this:
{
"accounts": {
"MediaFire": {
"provider": "MediaFire",
"usedStorage": "779680",
"totalStorage": "53687091200"
},
"4Sync": {
"provider": "4Sync",
"usedStorage": "620692",
"totalStorage": "16106127360"
}
}
}
I use a pasing function (which I can't control), which returns the parsed JSON response in an object result.
When I try to access the 4Sync like this:
var usedStorage = result.accounts.4Sync.usedStorage; //doesn't work
it doesn't work, I think it's because of the 4 at the beginning... The same operation with the other object works fine:
var usedStorage = result.accounts.MediaFire.usedStorage; //works
I know the result object contains the object 4Sync, but I can't access it. Here is a screenshot of Chrome's console:
Is there any workaround to solve this?
Use square brackets:
var usedStorage = result.accounts["4Sync"].usedStorage;
Property identifers can begin with a number, but member expressions with the . character will only allow valid variable identifiers (since anything else is ambiguous). To get around this, you can use the square bracket syntax, which is equivalent but allows the use of any string.
If you're interested, here is the grammar:
MemberExpression :
PrimaryExpression
FunctionExpression
MemberExpression [ Expression ]
MemberExpression . IdentifierName
Notice how square brackets can contain any expression, but the . can only be followed by an IdentifierName (basically, any valid identifier, plus reserved words in ES5).

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);

javascript problem: var a="1";b={a:a},the b variant isn't {"1":1}

var a="1";
b={a:a},
the b variant isn't {"1":1},why will this happened?
also want to know if i want to get the result what i want ,how can i solve this problem
JavaScript has a syntax quirk where the bit on the left-hand side of the : in an object literal isn't an expression like the bit on the right-hand side.
It can be either a quoted string literal, or an identifier token. In the case of an identifier, the token is taken verbatim, as if it were a quoted string. So {a:"1"} is the same as {"a":"1"}.
If you want to use an expression as a property name, you have to do so using the [] property access operator:
var b= {};
b[a]= a;
because b is assigned to an object literal that has an 'a' property with the value of whatever is in the a var, which is 'i'. This is how javascript works when defining object literals
var x = { prop : value }
even if you defined a var prop before you assign x, 'prop' is the literally (pun intended) a key in the object literal.
Also, i think you wanted
var b = {a:a};
The first a in your javascript object is a literal; it will not be treated as a variable and evaluated.

javascript string exec strange behavior [duplicate]

This question already has answers here:
Why does a RegExp with global flag give wrong results?
(7 answers)
Closed 8 months ago.
have funciton in my object which is called regularly.
parse : function(html)
{
var regexp = /...some pattern.../
var match = regexp.exec(html);
while (match != null)
{
...
match = regexp.exec(html);
}
...
var r = /...pattern.../g;
var m = r.exec(html);
}
with unchanged html the m returns null each other call. let's say
parse(html);// ok
parse(html);// m is null!!!
parse(html);// ok
parse(html);// m is null!!!
// ...and so on...
is there any index or somrthing that has to be reset on html ... I'm really confused. Why match always returns proper result?
This is a common behavior when you deal with patterns that have the global g flag, and you use the exec or test methods.
In this case the RegExp object will keep track of the lastIndex where a match was found, and then on subsequent matches it will start from that lastIndex instead of starting from 0.
Edit: In response to your comment, why doesn't the RegExp object being re-created when you call the function again:
This is the behavior described for regular expression literals, let me quote the specification:
ยง 7.8.5 - Regular Expression Literals
...
The object is created before evaluation of the containing program or function begins. Evaluation of the literal produces a reference to that object; it does not create a new object.
....
You can make a simple proof by:
function createRe() {
var re = /foo/g;
return re;
}
createRe() === createRe(); // true, it's the same object
You can be sure that is the same object, because "two regular expression literals in a program evaluate to regular expression objects that never compare as === to each other even if the two literals' contents are identical", e.g.:
/foo/ === /foo/; // always false...
However this behavior is respected on all browser but not by IE, which initializes a new RegExp object every time.
To avoid this behavior as it might be needed in this case, simply set
var r = /...pattern.../g;
var m = r.exec(html);
r.lastIndex=0;
This worked for me.

Categories

Resources