This function counts number of all characters of a string into a string. I am not fully understanding the if statement here and how it works.
function getFrequency(string) {
var freq = {};
for (var i=0; i<string.length;i++) {
var character = string[i];
if (freq[character]) {
freq[character]++;
} else {
freq[character] = 1;
}
}
return freq;
};
I thought freq[character] is the property of the object such as A B how does it work with if(freq[character]) also how does the increment of freq[character]++ works?
I have made test like this to try and understand it.
var v = {};
v.h = 3;
v["h"]++;
v["h"] = v["h"] + 1;
v.h++;
v.h = v.h + 2;
console.log(v);
console.log(v["h"]);
I think I can guess the if statement works that if the property exists but I thought JS has an object property calls .hasOwnProperty shouldn't this be used instead?
As for the increments, to my test, it works but I just don't get the reason.
Can someone give me a hand to elaborate this?
Thanks in advance
In javascript, objects are associative arrays. And vice versa. There is no difference between the two concepts.
So defining this variable as an empty object:
var freq = {};
... is actually creating an associative array (like a dictionary or map) with no keys added yet.
Moving on, let's take an input string like eek. The code here will look at the first letter and treat freq[character] the same as freq['e'], which is the same as freq.e.
In this code, the initial value of any letter in the freq object is undefined. So that initial if() check for the first "e" in our string actually looks like this:
if(undefined)
Javascript has the concept of "truthy" and "falsy" values; anything in javascript can be evaluated as a boolean, and (in most cases) a sensible result is achieved. Looking at undefined, Javascript will simply treat this a falsy value, fall to the else block, and therefore execute this code:
freq[character] = 1;
As already established, this is the same thing as freq.e = 1;
Now when the loop continues to the next letter (also an "e"), javascript will end up evaluating the expression if (1). Javascript treats this and all other non-zero numbers as "truthy", so this time will execute the following line:
freq[character]++;
Again, that's the same as freq.e++, where freq.e had a value of "1" that can now be incremented to "2".
One more time through the loop for the final letter "k". This time we get freq.k, which is still undefined. Undefined is falsy, so control falls to the else block, and freq.k is set to "1".
Now you can see how we can start to increment letters in the array as you find them in the string, even though it appears that you never defined an array in the first place, never set any values to anything other than "undefined", and never had a real boolean value to check against.
if (freq[character]) checks if the value is "truthy". That is, it's not false, null, undefined or 0. The first time we encounter any character the value will be undefined, since the object literal is created empty, so the "truthy" check will fail and control will fall to the else block.
So when we first see a specific letter set the value to 1 (which is now "truthy").
Any subsequent encounters of that letter just increment the value, which would be equivalent to say freq[character] = freq[character] + 1;. The increment syntax is just a shorthand.
if (freq[character]) checks if the object freq has a property on it with the value of character as the name. The result of this expression evaluates to true or false.
It can be more explicitly stated, as the following non-exhaustively illustrates:
if (freq[character] == null)
or
if (typeof freq[character] === 'undefined')
The danger in not being explicit when evaluating if an object is undefined or null, is if it is actually set to a different type that evaluates to true or false (0, 1, '0', true, false).
Related
Unfortunately in this case 0 is false, so I can’t simply say if (x)
I’m hoping for something shorter than this to improve my code golf answer that’s shorter than these options
// check explicitly for 0
x||x==0
// isNaN
!isNaN(x)
I could remove the ! in isNaN by inverting the if else logic but the former is shorter anyway. Anything better?
UPDATE: It can be assumed that x is either undefined or a number. It by definition won’t be other truthy values such as the empty string.
To give a little more context I’m saving numbers (which will for certain be numbers do to problem restrictions) to an object than later checking that object at specified indices to see if it contains a number or undefined at the specified index.
You could check if it's nullish (null or undefined) using the ?? operator.
For example these two are equivalent:
if (x != null) {
/* statement */
}
x ?? /* statement */
In your specific case, you can also use it when assigning a value (x) onto your object (o) if the property (z) is not nullish.
o.z ??= x;
Got it! I can loosy compare against null on an object I have elsewhere.
i.e.
/// some object o I happened to have declared
/// Some random property I don't have on it such as z
x!=o.z
/// compiles to the following
x!=null
/// which will be false for numbers and true for undefined
Why does the first line work while the second line throws run-time exception?
The first line:
[[]][0]++; //this line works fine
The second line:
[]++; //this lines throws exception
[[]][0]++
is equivalent to
var tmp = [[]];
tmp[0] = tmp[0]+1;
tmp[0] is an empty array, which is cast to the number 0, which increments to 1.
This only works because <array>[<index>]++ looks valid. It takes some type juggling, but it gets there.
But []++ is outright invalid. There's no way to make it make sense.
[] = []+1;
The left-hand side here is indeed invalid. You can't assign to an empty array.
The ++ operator (or indeed any postfix operator) requires the operand to be a "reference" - that is, a value that can be assigned to. [] is a literal, so you can't assign to it. [[]][0] is a valid reference to an element of a temporary array.
0++; // not valid, `0` is a literal.
var a = [];
a++; // ok, `a` is assignable
This is a rare case in which Javascript does something that actually makes sense. Consider
x[3]++; // Valid
3++; // Not valid
If this make sense for you, then what is surprising about
[[]][0]++; // valid
[]++; // not valid
<array>[index] is "a place" that you can assign or increment. That's all. The fact that you can increment a[<expr>] doesn't imply that you can increment <expr>.
The absurd part is that you can use [] as an index, that has the meaning of converting the array to an empty string "" and then to the number 0, but this is the well known problem of absurd implicit conversions of Javascript. Those implicit conversion rules are a big wart of Javascript and for example imply that 1 == [1] or that both []==false and (![])==false.
Javascript is pure nonsense in a lot of places... but not really here.
hope you can help. I've got an empty array in my project which fill up as certain buttons are pressed (using push()). I want to know when a certain set of elements are in the array.
In the below code, it seems to work, the 3 elements are all in the array so it prints 'yes'. If I take out the last element ("TR"), it prints 'nope'. However, if I take out either of the first 2 elements, it prints 'yes'. It seems to be only focusing on the last element in the includes() function.
Is there any way to have the include() or something similar, check to see if all elements are in my array? Keep in mind that the array could have many more elements and they won't be sorted.
Thanks in advance.
var arr = ["TL", "TM", "TR"];
if (arr.includes("TL" && "TM" && "TR")){
console.log("yes");
} else {
console.log("nope");
}
The issue is in your if statement because includes() returns a boolean based on the string parameter. A better way of doing this would be to use something like:
if(arr.includes("TL") && arr.includes("TM") && arr.includes("TR")) {
console.log("yes");
}
If you have lots of elements in your array I would suggest something more along the lines of:
var flag = true;
for(i=0; i<arr.length; i++) {
if(!arr.includes(arr[i])) {
flag = false;
}
}
if(flag) {
console.log("yes");
}
Even though the above answers show methods to get your desired result, I'm surprised no one has addressed why your original attempt didn't work. This gets into some foundational rules that JavaScript follows: how functions are called, logical operator evaluation, and operator precedence.
Calling arr.includes()
First off, you have a function includes which takes a single string argument. You have given this argument an expression instead of a string, so it is going to evaluate the expression. If the evaluation produces a string, it will return that value. If it produces a different type, it will attempt to convert the result to a string. So to clear it up, you haven't given it 3 strings to look for, but one expression that will be evaluated and become the string you are looking for.
Logical Operator Evaluation
But what is the value of that string? In JavaScript, the logical operators work in a way that can shortcut and return one of the values being evaluated. In most cases, we'd be comparing boolean values, and get true or false from the evaluation, but we're working with strings here, not booleans. Strings in JavaScript can be evaluated as "truthy" or "falsy", the former being any string that has length and the latter being a string with no length (an empty string). With this in mind, the shortcut functionality of the logical AND && operator will look at the first value in the expression, and if that value is "falsy" it will return that value. If that value is "truthy" it will look at the other side of the expression and return its value.
MDN describes this logic pretty well. Given expr1 && expr2 here's the logic:
Returns expr1 if it can be converted to false; otherwise, returns expr2. Thus, when used with Boolean values, && returns true if both operands are true; otherwise, returns false.
Order Precendence
Finally, a note on order precedence. Logical AND && is of equal precendence to itself, so your expression will read from left-to-right. If, say, your expression was "TL" || "TM" && "TR" the "TM" && "TR" expression would be evaluated first since logical AND && has a higher precendence than logical OR ||.
Evaluating Your Expression
Knowing all of this, we can pick apart what your expression is doing:
"TL" && "TM" && "TR" is comprised of all logical AND operators, so we will read this from left-to-right, starting with "TL" && "TM". Since "TL" is a truthy string, the other side of the expression is returned which is "TM". The next expression is then "TM" && "TR", of which "TM" is a truthy value, so "TR" is returned. In the end, the includes function is checking if the value of "TR" exists in the array, and ultimately returns true.
Again, do mark one of the others as answers here. Looping through the values you want to search for in the array is what you're looking for, and writing your own loop or using reduce accomplishes that, but I wanted to explain why your initial attempt probably seemed odd and clear up just what was happening.
This can be done cleanly using the reduce method of arrays. Here's how to do it with your example:
var arr = ["TL", "TM", "TR"];
var fails = ["TL"] // This will return false
var succeeds = ["TL", "TM", "TR"] // This will return true
var includesAll = (array_to_check) => arr.reduce(
(accumulator, current) => accumulator && array_to_check.includes(current),
true
)
if (includesAll(succeeds)){
console.log("yes");
} else {
console.log("nope");
}
The book says:
To run over a list (in listToArray and nth), a for loop specification like this can be used:
for (var node = list; node; node = node.rest) {}
Every iteration of the loop, node points to the current sublist, and the body can read its value property to get the current element. At the end of an iteration, node moves to the next sublist. When that is null, we have reached the end of the list and the loop is finished.
Question 1: Could you explain how the condition of the for loop works? I understand that it is checking whether the node(the current list) is null.. but how does the "node" argument by itself work?
Question 2: why doesnt the following code work?
function listToArray(list){
var result = [];
while(list.value != null){
result.push(list.value);
list = list.rest;
}
return result;
};
console.log(listToArray(list));
To understand how this works, you need to know two things:
How java script For loop works.
What are truthy values.
The syntax for the for loop statement:
for ([initialization]; [condition]; [final-expression]) statement
[condition] An expression to be evaluated before each loop iteration.
If this expression evaluates to true, statement is executed. If the
expression evaluates to false, execution skips to the first expression
following the for construct.
In Java script, a truthy value evaluates to true when evaluated in a Boolean context.
All values are truthy unless they are defined as falsy (i.e., except
for false, 0, "", null, undefined, and NaN).
So till node is null at one point, we may say that node is truthy and evaluates to true.
When the statement, node = node.rest assigns a null value to node, there the for loop exits.
The below code does not work because, list.value may be undefined, or as a fact any other falsy value other than null.
function listToArray(list){
var result = [];
while(list.value != null){
result.push(list.value);
list = list.rest;
}
return result;
};
console.log(listToArray(list));
instead try, while(list.value).
...but how does the "node" argument by itself work?
node is a variable. A variable is resolved to its value. E.g. if I have
var foo = 3;
alert(foo + 1);
it will resolve foo to the value 3 and then add 1 to it. Similarly in this case, node is resolved to whatever value it has, that value is converted to a Boolean value (i.e. Boolean(node)) and then tested whether it is true or false.
If the value of node is null, then Boolean(node) will return false.
why doesnt the following code work?
Can't really tell without knowing what list is and what exactly "does not work".
But presumably, list.rest is null at some point, in which case you would try to access null.value which throws an error. The equivalent version to the for loop would be to compare list itself:
while (list != null)
A simpler way is to use:
var result = Array.prototype.slice.call(list);
this will turn the list into an array
function listToArray(listValue){
var arrayResult = [];
while(listValue.list){
arrayResult.push(listValue.value);
listValue = listValue.list;
}
arrayResult.push(listValue.value);
return arrayResult;
}
I had read in some articles that in some languages, like in JavaScript, assignment operators can be used in conditional statements. I want to know what is the logic behind that operation? As far as I know, only comparison operators are allowed in condition checking statements.
Any expression is allowed in a condition checking statement. If the value of the expression isn't boolean, then it will be converted to boolean to determine what the statement should do.
You can for example use a number in an if statement:
if (1) { ... }
Any non-zero number will be converted to true.
In Javascript an assignment is also an expression, i.e. it has a value. The value of the expression is the same value that was assigned to the variable.
So, you can use an assignment expression in a condition checking statement, and the value of the expression is converted to boolean if needed:
if (x = 1) { ... }
Using an assignment in an condition checking statement can be useful, if the value that you assign should be used to control what happens. If you for example have a function that returns different values for the first calls, then a null when there are no more values, you can use that in a loop:
while (line = getLine()) {
document.write(line);
}
You can of couse do that with the assignment separated from the logic, but then the code gets more complicated:
while (true) {
line = getLine();
if (line == null) break;
document.write(line);
}
In JavaScript (and many other languages), when a value is assigned to a variable, the value "returned" is the value that was assigned to a variable. As such, such a statement can be used in a condition with any assigned value being evaluated in the standard way.
For example:
var y = 0;
if(x = y){
alert("Y(and thus X) is Truthy");
}
else{
alert("Y(and thus X) is Falsy");
}
There are two factors that combine to give this effect:
in many languages, including JavaScript, an expression of the form left = right evaluates to the new left. For example, a = b = c = 0 sets all of a, b, and c to zero.
in many languages, including JavaScript, a wide variety of values can be used as conditional expressions. if(7) is equivalent to if(true); so if(a = 7) is equivalent to a = 7; if(true) rather than to the presumably-intended if(a == 7).
Assigning a value with = returns that value. You can use it to make an assignment while testing if the outcome is truthy or falsey (null, 0, "" undefined, NaN, false)
if (myVar = myArgument) ...
//same as:
// myVar=myArgument;
// if (myArgument) ...
This assigns myArgument to myVar while testing myArgument. Another more specific example:
If (myVar = 3+2) ...
// same as:
// myVar=3+2;
// if (5) ...
The benefit is more compact, terse code, sometimes at the expense of clarity.
This could be used to make a condition check and also use the value after it, without writing more lines.
if (value = someFunction()) {
...
}
This is valid syntax, though highly discouraged. In quite a few languages this is explicitely forbidden, but some languages also does not make this rule (e.g. C).