What is the guard statement on this loop? - Javascript - javascript

I am working on a refactor of some older js and came across this loop:
var components = str.split('.');
var previousComponent = global;
for (var i = 0, component; component = components[i]; i++) {
if (typeof previousComponent[component] === "undefined") {
previousComponent[component] = {};
}
previousComponent = previousComponent[component];
}
I do not see how the guard statement says that this look ends. Could someone please explain this to me?

The condition component = components[i] "returns" component. It is undefined when i == components.length, therefore ending the loop.
As Jim Cote mentioned, another condition is if str has 2 dots together, then component can be an empty string, and also end the loop.

If your question is about when the loop ends, it ends when i is equal to the length of the array.
The condition part of your for statement is component = components[i] which assigns the value in components[i] to component and then returns that value to calling context - which is your for loop.
At the end of the array, when i === components.length, components[i] will be undefined which is not truthy, and hence breaks the loop.

In the for loop itself:
for (var i = 0, component; component = components[i]; i++) {
Note the part that says component = components[i].
This is not the equality comparison that we would typically find here, because it is =, not == or ===.
When the RHS (components[i]) is falsy, the loop will break.

Related

Javascript - Clarification on HashTable

I was watching a video on how to create a function to replace all duplicates in an array. For example, if an array consists of [1,1,2], the function will output simply [1,2].
After watching the video , the final code was :
let removeDuplicates = (a)=>{
let input = {};
let output = [];
for (let i = 0; i < a.length; i++){
let el = a[i];
if (!(el in input)){
input[el] = true;
output.push(el)
}
};
return output;
}
I understand majority of the function, but I'm having trouble understanding how the HashTable Input is used during the if statement.My questions are
1) how can we check if el isn't in input we input is never used previously in the function.
2) why do we set input[el] = true .
I'm sorry if this is not the clearest question in the world. I just can't get this question out of my head. Thanks in advance for the help.
input starts off empty {}, then after the 1st loop looks like {"1": true} then after the second loop, nothing has changed, after the 3rd loop looks like {"1": true, "2": true}
The code would still work even if we changed it to any of the following:
input[el] = undefined;
input[el] = null;
input[el] = el;
input[el] = "blah";
input[el] = "";
input[el] = false;
input[el] = 0;
because input contains a key "1" then the value 1 will never be added to the output array more than once. The test which handles this could also be changed from:
if (!(el in input)){
to:
if( !input.hasOwnProperty(el) ) {
See: Object.prototype.hasOwnProperty
You could also log the content of input at the end of the for loop to see what's going on like this:
console.log(JSON.stringify(input));
I've used JSON.stringify to avoid any 'helpful' browser consoles updating the content of previously logged objects.
Basically, play with the code to see what happens. If you only ever read how to code, you'll never be able to code.
Here's a snippet of the code you can run/modify to see how it works:
let a = [1, 1, 2];
let input = {};
let output = [];
console.log(JSON.stringify(input));
for (let i = 0; i < a.length; i++){
let el = a[i];
if (!(el in input)){
//if (input.hasOwnProperty(el))){
input[el] = true;
//input[el] = undefined;
//input[el] = null;
//input[el] = el;
//input[el] = "blah";
//input[el] = "";
//input[el] = false;
//input[el] = 0;
output.push(el)
}
console.log(JSON.stringify(input));
}
console.log(output);
and just for completeness you could also do away with the pseudo-hash input object by simply doing the following (although performance wise is probably much slower for big arrays):
let a = [1, 1, 2];
let output = [];
for (let i = 0; i < a.length; i++){
if (output.indexOf(a[i]) === -1 ){
output.push(a[i])
}
}
console.log(JSON.stringify(output)); // [1,2]
input is declared outside of the for loop, therefore it existed before the function and still exists inside the for loop scope throughout its course.
More info about the let keyword and its scoping.
We could set it any value we want, just so there's an el key in input
Basically, the code is using the input variable as a way to hold all the values already seen as we iterate over the passed in a array. Input starts off as empty, and gets populated as we iterate thru a. It is in scope from the perspective of the for loop.
The code sets input[el] = true, but actually you could set input[el] to anything and have the code still work. All the conditional !(el in input) is checking is that input has an object with a key of el. The value of that key does not matter in this case.
Using your example where a is [1,1,2]...
You begin with having the input object as empty.
Then, you take in the first element of a, 1, and set el = 1.
if 1 is not in input (it's not), you set input[1] = true, and push 1 onto the output array.
Next, you take in the second element 1 and set el = 1.
!(el in input) at this point is false since 1 is already in the input object. you ignore this and continue to the next element in a.
finally, you check 2 and set el = 2. if 2 is not in input (it's not), you set input[2] = true, and push 2 onto the output array.
A more straightforward way to do this is to convert the array to a set.
Or alternatively, you could just use the output array and use the indexOf method on each element of your passed in a variable to see if it already exists in output - if not, add it. otherwise continue.
Good question, other people may be asking the same thing. Actually your second question answers your first question once you understand how it works, so lets start there.
2) The first thing to understand is what we are concerned about is having the output array returned without any duplicates, so we set input[el] = true, because we want this to occur. Any value that makes it through the if check, is added to input object, then to our desired output. The input behaves similar to a HashTable or Set in this case:
1)With that understanding, we can see that input object is being declared outside of the loop but within the function, so it's entire purpose is just a container(Set) to check against, via the if check, before we add an item to our output.
This can be tested out by:
let input = {};
input["One"] = true;
console.log(input); //outputs {One: true}

Check if the first letter of each element of an array is capital using filter method. But I keep getting the whole array

// JavaScript Document
"use strict";
var testArray =["beau","Diane","morgan","Brittany"];
function checkCapital(value,index,array) {
if(value!==value.charAt(0).toLowerCase())
return value;
}
var capital =testArray.filter(checkCapital);
console.log(capital);
I need to check if the first letter of every value of the array is a capital using filter method. But I keep getting the whole array returned on my function.
Well, you are comparing an entire string with the lowercase equivalent of its first character, which is a problem. And you are going to return undefined when your condition is not satisfied, and a specific character if it is, which is a little strange. Your filter function should be
return value.charAt(0) !== value.charAt(0).toLowerCase()
or use the more modern
return value[0] !== value[0].toLowerCase()
and write the whole app in more modern JavaScript as follows:
const testArray = ["beau", "Diane", "morgan", "Brittany", "^&#SYMBOLS$$--"];
const capital = testArray.filter(s => s[0] !== s[0].toLowerCase());
console.log(capital);
but really, what is and is not an uppercase letter is an interesting problem in its own right. What if a word starts with a comma? A Cherokee or Devanagari letter? Letter-like symbols? If you really want to do uppercase properly, you might want to get a package like xregexp then you can write a filter function that matches XRegExp("^\\p{Lu}"). I understand this might be beyond what you are looking for at the moment, but it's a good thing to know about.
The filter() method depends on the return statement of the callback, which if returns true then filter() will include the current value to the array it will return. Just remove your if statement and put the condition on the return statement:
function checkCapital(value,index,array) {
return value !== value.charAt(0).toLowerCase();
}
Because your original callback returns true always, it would include all elements of the original array to be returned by the filter() method.
First off you are always returning value from your filter function, that is the core reason why you are getting the whole array. A filter function shold return true or false depending on whether the value should or should not be included in the output.
You have other issues with your code but try and solve them on your own.
solution :-
function checkUppercase(arr)
{
var array1 = arr.split(' ');
var newarray1 = [];
for(var x = 0; x < array1.length; x++){
newarray1.push(array1[x].charAt(0).toUpperCase()+array1[x].slice(1));
}
newarray1.join(' ');
let i=0;
if(JSON.stringify(array1) == JSON.stringify(newarray1)) {
return true
}else {
return false;
}
}
console.log(checkUppercase("solid Is Transparent"));

I would LOVE to add a value to an array please

I want to add the value "peanuts" to the bag array in Java Script. Any ideas where I'm going wrong?
var bag = [];
{
for (i=0; i<bag.length; i++) {
bag.push([i] === "peanuts");
}
}
First of all you are looping over an empty array. This loop will never iterate since the array is being defined right over it without being filled before the loop.
Second, what you're pushing into the array is in fact the result of the condition [i] === "peanuts" which if it was a valid condition, will result in a boolean value.
As #Tushar said, you should use bag.push("peanuts") to add the string "peanuts" to the array bag. So let's say your code could be like this(unless you describe what kind of condition you want inside the for loop):
var bag = [];
bag.push("peanuts");
By the way, you're using an unnecessary scope block:
var bag = [];
{ // <-- Unnecessary scope block
for (i=0; i < bag.length; i++) {
bag.push([i] === "peanuts");
}
} // <-- Unnecessary scope block
Some push() examples.
This example adds peanuts to the bag array using the push command and then checks if the element is in the array using the indexOf function. This last function will return -1 if the element is not in the array, otherwise it returns the first index position the element was found at
var bag = [];
bag.push("monkeys")
bag.push("peanuts")
bag.push("bananas")
if (bag.indexOf("peanuts") >= 0) {
document.write("Found my peanuts at index " + bag.indexOf("peanuts"))
}

Cannot set property '0' of undefined in 2d array

I know this has been asked a lot of times, but how do I fix exactly this thing?
I have a map[][] array (contains tile ids for a game) and I need to copy it to pathmap[][] array (contains just 0's and 1's, it is a path map), however when I do so..
function updatepathmap(){
pathmap = [];
var upm_x = 0;
while (upm_x < map.length){
var upm_y = 0;
while (upm_y < map[upm_x].length){
pathmap[][]
if (canPassthrough(map[upm_x][upm_y])) {
pathmap[upm_x][upm_y] = 1;
} else {
console.log(upm_x);
console.log(upm_y);
pathmap[upm_x][upm_y] = 0;
}
upm_y++;
}
upm_x++;
}
console.log(map);
console.log(pathmap);
}
..it gives me Cannot set property '0' of undefined typeerror at line pathmap[upm_x][upm_y] = 0;
Despite the foo[0][0] syntactic sugar, multi-dimensional arrays do not really exist. You merely have arrays inside other arrays. One consequence is that you cannot build the array in the same expression:
> var foo = [];
undefined
> foo[0][0] = true;
TypeError: Cannot set property '0' of undefined
You need to create parent array first:
> var foo = [];
undefined
> foo[0] = [];
[]
> foo[0][0] = true;
true
You can determine whether it exists with the usual techniques, e.g.:
> var foo = [];
undefined
> typeof foo[0]==="undefined"
true
> foo[0] = true;
true
> typeof foo[0]==="undefined"
false
I would have thought pathmap[][] was a syntax error, I'm surprised you're not seeing one.
Before you can use an array at pathmap[upm_x], you must create an array at pathmap[upm_x]:
pathmap[upm_x] = [];
This would be the first line in your outer while, so:
while (upm_x < map.length){
pathmap[upm_x] = [];
// ...
Remember that JavaScript doesn't have 2D arrays. It has arrays of arrays. pathmap = [] creates the outer array, but doesn't do anything to create arrays inside it.
Side note:
var upm_x = 0;
while (upm_x < map.length){
// ...
upm_x++;
}
is an error-prone way to write:
for (var upm_x = 0; upm_x < map.length; upm_x++){
// ...
}
If you use while, and you have any reason to use continue or you have multiple if branches, it's really easy to forget to update your looping variable. Since looping on a control variable is what for is for, it's best to use the right construct for the job.
Side note 2:
Your code is falling prey to The Horror of Implicit Globals because you don't declare pathmap. Maybe you're doing that on purpose, but I wouldn't recommend it. Declare your variable, and if you need it outside your function, have your function return it.
Side note 3:
map would make this code a lot simpler:
function updatepathmap(){
var pathmap = map.map(function(outerEntry) {
return outerEntry.map(function(innerEntry) {
return canPassthrough(innerEntry) ? 1 : 0;
});
});
console.log(map);
console.log(pathmap);
}

In JavaScript, is there an easier way to check if a property of a property exists?

Is there an easy way to natively determine if a deep property exists within an object in JavaScript? For example, I need to access a property like this:
var myVal = appData.foo.bar.setting;
But there is a chance that either foo, foo.bar, or foo.bar.setting has not been defined yet. In Groovy, we can do something like this:
def myVal = appData?.foo?.bar?.setting
Is there a similar way to do this in JavaScript, without having to write a custom function or nested if statements? I've found this answer to be useful, but was hoping there was a more elegant and less custom way.
I find this very convenient:
var myVal = (myVal=appData) && (myVal=myVal.foo) && (myVal=myVal.bar) && myVal.settings;
If a property exists, the next part of the sequence will be attempted.
When the expression before && evaluates to false, the next part of the expression will not be checked. If either of myVal.appData.foo.bar.settings is not defined, the value of myVal (undefined( will evaluate to false.
Sorry, it's not great:
var myVal = appData && appData.foo && appData.foo.bar && appData.foo.bar.setting;
Another option:
try {
var myVal = appData.foo.bar.setting;
} catch (e) {
var myVal = undefined;
}
The . operator is not really intended for accessing objects like this. Probably using a function would be a good idea.
The optional chaining operator (?.) was introduced in ES2020. Now, you should be able to write:
const myVal = appData?.foo?.bar?.setting
I find other approaches a bit immense. So, what would be the major drawback of the following approach:
// Pass the path as a string, parse it, and try to traverse the chain.
Object.prototype.pathExists = function(path) {
var members = path.split(".");
var currentMember = this;
for (var i = 0; i < members.length; i++) {
// Here we need to take special care of possible method
// calls and arrays, but I am too lazy to write it down.
if (currentMember.hasOwnProperty(members[i])) {
currentMember = currentMember[members[i]];
} else {
return false;
}
}
return true;
}
Basically, we define a method on the object (not necessarily) and that method takes the path to a nested object and returns existence confirmation, likeappData.pathExists("foo.bar.setting");
EDIT:
Check object[prop] == undefined is not semantically correct since it will return false even if the property is defined although its value is undefined; that is why I use hasOwnProperty to check is the property defined. This might not be important if one needs to just fetch the value.
If, after:
var myVal = appData.foo && appData.foo.bar && appData.foo.bar.setting;
myVal is not undefined, it will hold the value of appData.foo.bar.setting.
You can try this
var x = {y:{z:{a:'b'}}}
x && x.y && x.y.z && x.y.z.a //returns 'b'
This is not as good as the groovy expression but it works. The evaluation stops after encountering the first undefined variable.
var product = ...,
offering = (product||{}).offering,
merchant = (offering||{}).merchant,
merchantName = (merchant||{}).name;
if (merchantName)
displayMerchantName(merchantName);
http://osteele.com/archives/2007/12/cheap-monads
I just cooked this up so it might not work exactly right, I've also included two test cases.
function hasPropertyChain(o, properties) {
var i = 0,
currentPropertyChain = o;
if(!o) {
return false;
}
while(currentPropertyChain = currentPropertyChain[properties[i++]]);
return i - 1 === properties.length;
}
alert(hasPropertyChain({a:{b:{c:'a'}}}, ['a','b','c'])); // true
alert(hasPropertyChain({a:{b:{c:'a'}}}, ['a','b','c', 'd'])); // false

Categories

Resources