Receiving "undefined" errors when trying to access object details - javascript

I'm very much a beginner when it comes to javascript, so I hope that you will bear with me.
I have an html file that contains a table with three values for reporting on a variety of system statuses - name, status, and notes. I am pulling the contents of the table into a variable, and scanning it for tags that point to an offline or partially online system. If found, the system name, status, and any notes concerning its status are stored into an object array (at least, I think it is an object array - I'm new at this).
Here's where I am having trouble. While I am looping through the table looking for a matching value, I am able to display an alert with the appropriate information as it is assigned to the object (example SmartPay, Offline, Not taking credit cards). Once I am out of the loop, I am unable to reference any data that I have stored. Here's the object constructor and function that I am working with:
function status(name, status, notes) {
this.name = name;
this.status = status;
this.notes = notes;
}
function parseStatus() {
var tagMatch = document.getElementsByTagName("td");
var problemStatus = new Array();
var j = 0;
for (i = 0; i < tagMatch.length; i++) {
if (tagMatch[i].textContent == "Offline" ||
tagMatch[i].textContent == "Partially Online") {
problemStatus[j] = new status(tagMatch[i-1].textContent,
tagMatch[i].textContent, tagMatch[i+1].textContent);
alert("OFFLINE: " + problemStatus[j]['name'] + ", " +
problemStatus[j]['status'] + ", " + problemStatus[j]['notes']);
j++;
}
}
}
The alert at the end of the for loop displays everything correctly. If I add this after the end bracket for the loop
alert(problemStatus[0]['name']);
I get "Cannot read property 'name' of undefined"
I do not understand where I am going wrong. Can someone please point me in the right direction?
Edited to include the link to JSFiddle

Related

Nested 'for' loop - array undefined

I am working on a JS where I want to create a simple game that starts by chosing number of players, name of each player and whether a player is a dealer or not. There can be only one dealer for each game:
function player(playerName, playerDealer) {
this.playerName = playerName;
this.playerDealer = playerDealer;
}
var playerNumber = prompt('Nr of players?');
var playersArray = [];
for (i = 0; i < playerNumber; i++) {
var j = i + 1;
var dealerAssigned = false; // control variable to check whether dealer has been assigned
var inputName = prompt('Name of player nr ' + j);
var inputDealer = prompt('Is player ' + inputName + ' also a dealer? (yes/no)');
playersArray[i] = new player(inputName, inputDealer);
for (k=0;k<playerNumber;k++){ // I want to go through the players array to check if dealer has been assigned
if (playersArray[k].playerDealer == 'yes') {
dealerAssigned=true;
break;
};
};
if(dealerAssigned){ //if dealer has been assigned, don't add the current player to the array and continue with the next iteration
alert("already assigned");
continue;
};
};
I need to include a simple test into the loop that would check if the dealer has been appointed. If so, I want the script only to alert 'already assigned' and skip to the next player. But I am constantly getting the following error
TypeError: playersArray[k] is undefined
Can anybody explain why is it undefined?/What am I doing wrong?
The bug you're specifically asking about appears to me to be that you're iterating over undefined array values, as the error you're getting suggests.
You're getting the number of players you want in line
var playerNumber = prompt('Nr of players?');
Then, you proceed to have two iterations (one nested in the other), in which the inner loop is trying to access values that haven't yet been assigned since the outer loop hasn't gotten there yet:
for (i = 0; i < playerNumber; i++) {
playersArray[i] = new player(inputName, inputDealer);
for (k=0; k < playerNumber; k++) {
if (playersArray[k].playerDealer == 'yes') {
...
}
}
}
It appears to me that the logical error here is the nested loop. I recommend just initializing all players in one loop, then verify that all players have an assigned dealer afterward.
I should add that I'm being intentionally myopic here and focusing very narrowly on the question asked and overlooking other issues I see.
Your for loop inside a for loop is iterating over an array that hasn't been filled yet.
First iteration playersArray[j] = new Player(...) makes the array [Player] or an array of one element! Yet the second loop is looking for an array of many elements. once you look for playersArray[1] but there is only playerArray[0] you get undefined and so undefined.playerDealer causes a TypeError.
`This is your structure stipped-down:
for (i = 0; i < playerNumber; i++) {
playersArray[i] = new player(inputName, inputDealer);
for (k=0;k<playerNumber;k++)...{
//anything with index k > i is undefined, since your outer loop
//hasn't initialized it yet.
}
}
It seems that your i-loop is trying to insert elements for the size of the array to be, but your k-loop is trying to also access the entire array instead of just the initialized portions. Limit this to for (k=0; k<i+1 ;k++) so you only check the previously initialized values of you playersArray

JavaScript Function to Pull related object fields

I am using the following query to try to pull fields off of a User lookup on the Account. There is a field on the Account called Dedicated_Rep__c which is a user lookup. I am building my button off the opportunity, and I want to be able to pull the Opportunity's Account's Dedicated Rep's First Name, Last Name, and Email. Here's my code:
function getDedicatedAccountRep (oppId) {
var result = sforce.connection.query("select Account.Id, Account.Dedicated_CS_Rep__r.FirstName from Opportunity where Id = '" + oppId + "' ");
if(!result || result['size'] != 1) {
return null;
}
var DedRepRole = result.getArray('records')[0];
return DedRepRole.Account;
}
var dedicatedRep = getDedicatedAccountRep('{!Opportunity.Id}');
I am getting an error:
Cannot read property 'Dedicated_CS_Rep__c' of undefined
I am referencing the code later in the button and I am instantiating it by putting: dedicatedRep.Dedicated_CS_Rep__r.FirstName
Start with something like that (I prefer the Google Chrome's javascript console, you can open it with Ctrl+Shift+J; but feel free to use Firefox + Firebug or IE's developer tools...)
{!requireScript("/soap/ajax/29.0/connection.js")}
var result = sforce.connection.query("SELECT Account.Dedicated_CS_Rep__r.FirstName FROM Opportunity WHERE Id = '{!Opportunity.Id}'");
console.log(result);
debugger;
This will let you inspect the query result and play with the results. I think your problem is that the full expression can look like this:
result.records.Account.Dedicated_CS_Rep__r.FirstName
A lot can go wrong here. result should be OK and records should be always 1 row since we run it for one Opportunity (let's ignore crazy scenarios where somebody deleted the Opp between you navigating to the page and clicking the button... But still:
the Account can be blank (out of the box it's perfectly valid to have private Opportunities; maybe your organisation has marked the field as required).
And similarly in theory it's valid to have Account without the user.
So - you have 2 chances to hit null pointer exception:
Therefore properly protected code would have this orgy of null / undefined checks:
{!requireScript("/soap/ajax/29.0/connection.js")}
var result = sforce.connection.query("SELECT Account.Dedicated_CS_Rep__r.FirstName FROM Opportunity WHERE Id = '{!Opportunity.Id}'");
console.log(result);
if(result != null
&& result.records != null
&& result.records.Account != null
&& result.records.Account.Dedicated_CS_Rep__r != null){
alert(result.records.Account.Dedicated_CS_Rep__r);
// return result.records.Account.Dedicated_CS_Rep__r;
} else {
alert('Nope');
// return null;
}

Dynamic array using variable

I am trying to dynamically access arrays, otherwise I would have to duplicate my code, when in reality the only difference would be buildablesets1 and buildablesets2.
var buildablesets1 = [""];
var buildablesets2 = [""];
var buildablesetsx = "buildablesets" + playerturn;
for (var i = 0; i < set.length; i++) {
if (doesOwnSet('player'+playerturn, set[i])) {
if(playerturn===1) buildablesets1.push(set[i]);
}
}
if (buildablesetsx.length > 1){
var b = $("#buildsets"+playerturn);
for (k = 1; k < buildablesetsx.length; k++){
b.append("<option value='" + buildablesetsx[k]+ "'>" + buildablesetsx[k] + "</option>");
}
}
The code pushes any sets owned by the player to buildablesetsX. Then the dropdown box #buildsetsX is populated with all the owned sets.
1. How do I get the effect of buildablesetsX where X depends on the players turn.
2. I want to depopulate the dropdown box on each turn otherwise it will duplicate, since it is appending. (would apreciate a better way of doing this, ideally I want to populate the dropdown box only if there is a new set).
Plus sorry I understand that this question has been asked before, but I didnt understand the answers or the question exactly.
Any time you find yourself creating variables with sequential numbers, and wanting to access them dynamically, what you really should be using is an array. In this case, you should use a two-dimensional array:
var buildablesets = [[""], [""]];
The first dimension is the player number, the second dimension is the list of sets that player has built.
To access a particular player's sets, do:
buildablesetsx = buildablesets[playerturn];
The rest of your code will work as you've written it, with the above variable assignment.

how do javascript objects constructed with both dot and literal notation behave when called?

While running the code below, without any function calls, I would immediately get this output
["1122","3rd St","Seattle","WA","92838"]
The closest thread that addressed this code was Need Explanation: Organization with Objects in a Contact List (Javascript, Codecademy) but it didn't quite address my concern.
I'm sure that the way I had added key,value pairs to the objects is somehow yielding this output, but I can't seem to explain why, especially when running the code, there is no function call included.
When actually trying to call search (e.g. search("steve")), it would fail but it would work on search("bill"). I thought it might be related to the javascript console but I checked using Chrome's console with the same results. Any help would be much appreciated.
var friends={};
friends.bill = {};
friends.steve={};
friends.bill["firstName"]="Bill";
friends.bill["lastName"]="Gates";
friends.bill.number="333.222.3937";
friends.steve["firstName"]="Steve";
friends.steve.lastName="Ballmer";
friends.steve["number"]="829.383.3939";
friends.bill["number"]="232.8392.2382"
friends.bill.address=['5353','Cook Ave','Bellevue','CA','94838']
friends.steve.address=['1122','3rd St','Seattle','WA','92838']
var search=function(name)
{
for(var i in friends){
if (name==i["firstName"])
{
console.log(friends[i])
return friends[i]
}
else{
return "no match"
}
}
}
try changing:
for(var i in friends){
if (name==i["firstName"])
...
to
for(var i in friends){
if (name == friends[i]["firstName"])
...
You meant something like:
for(var i in friends){
if( name == i ) { //i would be either "bill" or "steve" and check against name variable
console.log("Data for:" + i + " firstname is:" + friends[i]["firstName"]);
}
}
You are seeing this output:
["1122","3rd St","Seattle","WA","92838"]
Because it is what is currently stored in $_ (Most browser based JS interpreters will spit out what is in $_ on the prompt).
$_ is the value of the last evaluated expression, which in your case is:
friends.steve.address=['1122','3rd St','Seattle','WA','92838']
The "var search ..." is actually a declaration and will therefore not be stored in $_.
Also, your search function should probably look like this:
var search = function(name) {
for(var i in friends) {
if (name.toLowerCase() == friends[i]["firstName"].toLowerCase()) {
console.log(friends[i])
return friends[i]
}
}
return "no match";
};
#cherniv might have beat me to the punch on moving the return out.

Update a global variable in a Javascript function without using the variable name

I am working on a replica of an old game I played in High School called Drug Wars. I have global variables that hold the qty of each drug owned. Originally I just used a different function for each buy and sell operation for each drug. Trying to save myself some code and provide for some scale ability I wanted to change it to one function for buying, passing the drugqty, drugprice(done by a different function), and the drugname.
The part I am stuck with is trying to update the drugqty (used locally in the function) to the global variable.
function buy(drugqty, drugprice, drugname)
{
if (money < drugprice)
{
window.alert("You do not have enough money to do that!")
}
else if(money >= drugprice)
{
var maxdrug = money/drugprice;
var addtoqty;
addtoqty=prompt("How many would you like to purchase? They are $" + drugprice +" a unit. You can afford " + Math.floor(maxdrug) + "." );
if((addtoqty*drugprice)>money)
{
window.alert("Nice try you need more money to do that!")
}
else {
drugqty = parseInt(drugqty, 10) + parseInt(addtoqty, 10);
money = money - (drugprice*addtoqty);
document.getElementById(drugname+"qty").innerHTML=drugqty;
document.getElementById("money").innerHTML=money;
}
}
}
Below is my buy button for one particular drug.
<td><center><button onclick="buy(cocaineqty, cocaine, 'cocaine')">Buy</center></button></center></td>
When the button is pressed it calls the function and executes correctly.
I just need to figure out how to update the global variable cocaineqty with what the local function drugqty creates.
I tried things like
drugname+"qty"=drugqty;
My two thoughts were to parse that information again from where it is displayed or somehow update the global variable in the function(again using it for more than one drug in the future so it can be done dynamically)
<td id="cocaineqty" align="center">0</td>
My first post here but I am more than willing to make changes or correct any mistakes with the post. Thanks for your time!
edit: Updated my code for the correct information based on the checked answer
function buy(drugqty, drugprice, drugname)
{
if (money < drugprice)
{
window.alert("You do not have enough money to do that!")
}
else if(money >= drugprice)
{
var maxdrug = money/drugprice;
var addtoqty;
addtoqty=prompt("How many would you like to purchase? They are $" + drugprice +" a unit. You can afford " + Math.floor(maxdrug) + "." );
if((addtoqty*drugprice)>money)
{
window.alert("Nice try you need more money to do that!")
}
else {
drugqty = parseInt(drugqty, 10) + parseInt(addtoqty, 10);
money = money - (drugprice*addtoqty);
document.getElementById(drugname+"qty").innerHTML=drugqty;
document.getElementById("money").innerHTML=money;
window[drugname+"qty"]=drugqty;
}
}
Thanks again for all of your assistance!
Firstly they way this im implemented is not awesome. There is a lot that could be done better but Im not going to try to change you implementation here... :)
To do what you want to do, that is access a global variable without knowing its actual name, can be done like this.
window[drugname + "qty"] = drugqty;
This works because
"Global" variable in a browser are actually on the window object
objects in JavaScript are associative arrays. Which in simple terms means you can access its properties via it name as you would in a Key Value pair
You can use the eval() function for this:
eval(drugname+"qty"=drugqty);
Note: It would be much better to learn about Object Oriented programming. So you can make a Drug class and class instances (objects) of this class to store the particular drug information. It is easy pass around these classes and keep track of the information!
You could save all your quantities in a single object and add a property per drug.
var quantities = {};
quantities[drugname + "qty"] = drugqty;
But, if you want to display the results also, it might be easier to leave out the "qty"-part:
var quantities = {};
quantities[drugname] = drugqty;
And for displaying the results:
var html = "<table>";
for (var quantityName in quantities) {
if (quantities.hasOwnProperty(quantityName)) {
html += "<tr><td>" + quantityName + "</td>";
html += "<td id='" + quantityName + "qty' align='center'>" + quantities[quantityName] + "</td></tr>";
}
}
html += "</table>";

Categories

Resources