Simpy cannot iterate over javascript object? - javascript

I have scoured the other question/answer for this and implemented everything and I still cannot access the values of the object. Here's the code I am using:
function apply_voucher(voucher) {
var dates = $.parseJSON($("[name='dates']").val());
var voucher_yes_no = new Array();
var voucher_reduction = new Array();
if(voucher.length > 0)
{
$.each(dates, function(room_id, these_dates) {
$.post('/multiroom/check_voucher/'+voucher+'/'+room_id, function(result) {
if(result.result == 'ok') {
voucher_yes_no.push('yes');
voucher_reduction.push(result.voucher_reduction);
} else {
voucher_yes_no.push('no');
}
}, 'json');
});
// check if there are any yes's in the array
if('yes' in voucher_yes_no) {
console.log("no yes's");
} else {
console.log(voucher_reduction);
console.log(typeof voucher_reduction);
for (var prop in voucher_reduction) {
console.log(prop);
console.log(voucher_reduction[prop]);
if (voucher_reduction.hasOwnProperty(prop)) {
console.log("prop: " + prop + " value: " + voucher_reduction[prop]);
}
}
}
}
}
Apologies for the constant console logging - I'm just trying to track everything to make sure it's all doing what it should. The console output I get from this is below:
...which shows the object containing one value, "1.01" and my console.log of the typeof it to make sure it is actually an object (as I thought I was going mad at one point). After this there is nothing from inside the for-in loop. I have tried jquery's $.each() also to no avail. I can't understand why nothing I'm trying is working!

It does not work because the Ajax call is asynchronous!
You are reading the values BEFORE it is populated!
Move the code in and watch it magically start working since it will run after you actually populate the Array!
function apply_voucher(voucher) {
var room_id = "169";
var dates = $.parseJSON($("[name='dates']").val());
var voucher_reduction = new Array();
$.post('/multiroom/check_voucher/'+voucher+'/'+room_id, function(result) {
if(result.result == 'ok') {
voucher_reduction.push(result.voucher_reduction);
}
console.log(voucher_reduction);
console.log(typeof voucher_reduction);
for (var prop in voucher_reduction) {
console.log(prop);
console.log(voucher_reduction[prop]);
if (voucher_reduction.hasOwnProperty(prop)) {
console.log("prop: " + prop + " value: " + voucher_reduction[prop]);
}
}
}, 'json');
}
From what it looks like, you plan on making that Ajax call in a loop. For this you need to wait for all of the requests to be done. You need to use when() and then(). It is answered in another question: https://stackoverflow.com/a/9865124/14104

Just to say for future viewers that changing the way I did this to use proper deferred objects and promises, which blew my head up for a while, but I got there! Thanks for all the help, particularly #epascarello for pointing me in the right direction :) As soon as I started doing it this way the arrays began behaving like arrays again as well, hooray!
Here's the final code:
function apply_voucher(voucher) {
var booking_id = $("[name='booking_id']").val();
var dates = $.parseJSON($("[name='dates']").val());
if(voucher.length > 0) {
var data = []; // the ids coming back from serviceA
var deferredA = blah(data, voucher, dates); // has to add the ids to data
deferredA.done(function() { // if blah successful...
var voucher_yes_no = data[0];
var voucher_reduction = data[1];
if(voucher_yes_no.indexOf("yes") !== -1)
{
console.log("at least one yes!");
// change value of voucher_reduction field
var reduction_total = 0;
for(var i = 0; i < voucher_reduction.length; i++) {
reduction_total += voucher_reduction[i];
}
console.log(reduction_total);
}
else
{
console.log("there are no yes's");
}
});
}
}
function blah(data, voucher, dates) {
var dfd = $.Deferred();
var voucher_yes_no = new Array();
var voucher_reduction = new Array();
var cycles = 0;
var dates_length = 0;
for(var prop in dates) {
++dates_length;
}
$.each(dates, function(room_id, these_dates) {
$.post('/multiroom/check_voucher/'+voucher+'/'+room_id, function(result) {
if(result.result == 'ok') {
voucher_reduction.push(result.voucher_reduction);
voucher_yes_no.push('yes');
} else {
voucher_yes_no.push('no');
}
++cycles;
if(cycles == dates_length) {
data.push(voucher_yes_no);
data.push(voucher_reduction);
dfd.resolve();
}
}, 'json');
});
return dfd.promise();
}

Can you show how voucher_reduction is defined?
I am wondering where the second line of the debug output comes from, the one starting with '0'.

in this line:
console.log(vouncher_reduction[prop]);
^
The name of the variable is wrong (then) and probably that is breaking your code.

I think there are no problem with your loop.
But perhaps with your object.
Are you sure what properties has enumerable ?
Try to execute this to check :
Object.getOwnPropertyDescriptor(voucher_reduction,'0');
If it return undefined, the property was not exist.

Related

Storing items as cookies, works perfectly fine on a normal browser, but on a native device browser there are implications

I am creating a website for a client at the moment, we decided an easy way to store "items" which will be passed down to a subdomain from the root would be to store them as cookies. This works perfectly fine in a normal browser, yet when I tested it on a native device browser it didn't work as smoothly. I am wondering where some of these problems may have been coming from and hoping you wonderful developers can lend a man a hand.
The idea is that on the frontend when a "Your Order" side drawer is pressed, a function runs grabbing the cookies and then sorts them into their specified content area's -> Downloadable Content, Requested Material and Bespoke Content. I have created two separate functions for this, one that was the original working piece and another more tailored and "good practice".
Tried having the "Value" of the cookie containing the values that need to be stored such as, [itemname],[itemlocation], [itemdescription], [itemtype].
The second function stores the item data in an object, the object is then JSON.stringified and iterated over in a for loop. This is then taken out of a string with JSON.parse() and further iterated over in an .each() iterating over the index(key) and val(value).
FIRST FUNCTION:
$('section#review-downloads a.toggle-btn').bind('click tap', function() {
let cookies;
let itemSplit;
var section = $('section#review-downloads');
if(section.hasClass('active')) {
section.removeClass('active');
setTimeout(function() {
$('section#review-downloads .selected-items div').find('p').remove();
}, 900);
} else {
section.addClass('active');
$.each(document.cookie.split(';'), function() {
cookies = this.split('=');
let trimId = cookies[0].trim();
vals = cookies[1].replace(/[\])}[{(]/g, '');
if(!(cookies[0] === "envFilter")) {
$.each(vals.split('[ ]'),function() {
itemSplit = this.split(',');
let itemId = trimId;
let itemName = itemSplit[0];
let itemUrl = itemSplit[1];
let itemType = itemSplit[2];
let itemDesc = itemSplit[3];
if(itemType === ' Downloadable Content ') {
$('<p id="selected-item-'+itemId+'"><strong>'+itemName+'</strong>'+itemDesc+'</p>').appendTo('section#review-downloads .review-container .selected-items .downloadable-content');
} else if (itemType === ' Requested Materials ') {
$('<p id="selected-item"><strong>'+itemName+'</strong>'+itemDesc+'</p>').appendTo('section#review-downloads .review-container .selected-items .requested-material');
} else if (itemType === ' Bespoke Content ') {
$('<p id="selected-item"><strong>'+itemName+'</strong>'+itemDesc+'</p>').appendTo('section#review-downloads .review-container .selected-items .bespoke-content');
}
});
};
});
}
return false;
});
THE SECOND FUNCTION (best practice)
$('div.support-item-wrapper div.order-add').bind('click tap', function() {
let id = $(this).data('id');
let name = $(this).data('title');
let file = $(this).data('file');
let type = $(this).data('type');
let desc = $(this).data('description').replace(/(\r\n|\n|\r)/gm, "");
let url = $(this).data('url');
let cookieVal = {
name: name,
file: file,
type: type,
desc: desc,
url: url
};
let string = JSON.stringify(cookieVal);
setCookie('product-'+id, string, 1);
});
$('section#review-downloads a.toggle-btn').bind('click tap', function() {
var section = $('section#review-downloads');
if(section.hasClass('active')) {
section.removeClass('active');
} else {
section.addClass('active');
let decoded_user_product;
cookie_values = document.cookie.split(';');
for(i = 0; i < cookie_values.length; i++) {
cookie_split = cookie_values[i].split("=");
cookie_key = cookie_split[0].trim();
cookie_value = cookie_split[1].trim();
// console.log(cookie_value);
if(cookie_key != "envFilter") {
decoded_user_product = JSON.parse(cookie_value);
}
$.each(decoded_user_product, function(index, val) {
// console.log("index:" + index + "& val:" + val);
if(index === "name") {
console.log(val);
} else if (index === "type") {
console.log(val);
} else if (index === "desc") {
console.log(val);
}
});
}
// console.log(decoded_user_product);
};
});
On Desktop, the results are perfectly fine. Each item is easily console.log()'able and has been easily sorted in the FIRST FUNCTION.
On Mobile, the same results were as to be expected. But after realising it hadn't worked I used chrome://inspect along with a lot of console.logs to come to the conclusion that I may be too inexperienced to understand what parts of my code are unable to run on a native browser.

JS Array allways returns undefined and length = 0

(using Node.js)
Hi, I have an array with users (User class) on it, when I print the array with console.log, it shows the content correctly and shows that it's length is 3, but when i try to get any thing from the array, it returns undefined and for *.length, it returns 0. Where's the problem?
exports.users = [];
exports.loadUsers = (callback) => {
let more = true;
let i = 0;
while(more) {
let us = _usersFolder + "us_" + i + "/";
if(fs.existsSync(us)) {
fs.readFile(path.join(us + "personal.json"), (err, data) => {
if(err) {
console.log("failed to load file!");
return;
}
let json_personal = JSON.parse(data);
this.users.push(new User(json_personal));
});
i++;
} else {
more = false;
}
}
callback();
}
exports.getUserById = (id) => {
console.log(this.users);
console.log("length: " + this.users.length);
console.log(this.users[0]);
for(let i = 0; i < this.users.length; i++) {
let u = this.users[i];
console.log(u.id);
if(u.id === id) {
return u;
}
}
return false;
}
getUserById is called in the callback, so users are already loaded.
It depends on where you are using the 'this' object. It's possible that 'this' makes reference to a different object than the one you stored the array in ('this' varies depending on the scope where you are using it).
I'd need more information to help you.
var users=[{"num":1},{"num":2},{"num":3}];
console.log(this.users);
console.log("length: " + this.users.length);
console.log(this.users[0]);
output
(3) [Object, Object, Object]
length: 3
Object {a: 1}
I hope you are defining after you print in console.
var users = [];
console.log(users);
console.log("length: " + users.length);
console.log(users[0]);
users.push(1);
users.push(2);
users.push(3);
The output of console.log() is misleading; While you log that time there was no value. After that it was added. It prints the reference of object , not the state value. So you will see current state value all the time.

Javascript Array Losing an Element At Random

I have a very strange issue that I am running into. I am using jsTree from JQueryUI on one of my sites, and I have different implementations of it used in different .js files. One of them seems to work, which is very confusing as it uses almost identical code (only the variable names are different) to the implementation that is broken. The problem comes from the contextmenu function. The code I am using is as follows:
$(document).ready(function () {
if(typeof dryerList == 'undefined' || dryerList.length == 0) {
var dryerList = [];
$.ajax({
url:'../TrackingApp/getGrainBins.php?t=234.23423452353',
async: false,
success: function(text) {
try {
dryerList = $.parseJSON(text);
} catch (e) {
alert('ERROR: ' + e);
}
if(dryerList.length == 0) {
alert('ERROR: No fleet data received.')
}
}
});
}
$("#dryerListTree").jstree({
plugins : ['json_data', 'ui', 'themes', 'contextmenu'],
contextmenu: {items: customBinMenu},
json_data : { data: binNodes }
});
$('#dryerListTree').bind("dblclick.jstree", function (event) {
var node = $(event.target).closest("li");
var id = node[0].id;
for(i=0; i < dryerList.length; i++) {
if(id == dryerList[i].id) {
centerMap(dryerList[i].y, dryerList[i].x);
break;
}
}
});
});
function customBinMenu(node) {
if ($(node).hasClass("folder")) {
return;
}
var items = {
centerItem: {
label: "Locate",
action: function () {
// Centers map on selected bin
var id = node[0].id;
for(i=0; i < dryerList.length; i++) {
if(id == dryerList[i].id) {
centerMap(dryerList[i].y, dryerList[i].x);
break;
}
}
}
},
dashboardItem: {
label: "Dashboard",
action: function () {
// Opens dryer info window over map
var id = node[0].id;
var dryerIndex = -1;
for(i=0; i < dryerList.length; i++) {
if(id == dryerList[i].id) {
dryerIndex = i;
break;
}
}
}
}
};
return items;
}
The strange bit is, the double-click handler works just fine. When I get to the customBinMenu() function, the dryerList array is there, and dryerList[0] contains 4 of the 5 values that it should- but somehow the 'id' element has been dropped from that object. I have been looking at this for quite some time, and I can't figure out how it can drop a single element from the object without losing any other data, especially when identical code is working for a similar list. Any suggestions?
Ok, I read in your question: 'and dryerList[0] contains 4 of the 5 values that it should- but somehow the 'id' element has been dropped from that object'
So by 'element' and 'value' I assume you mean 'attribute': the node's 'id'-attribute to be precise ??
I see in your code: var id = node[0].id;
That should be: var id = node[0].getAttribute("id");
Good luck!
UPDATE 1:
Ok, if (as per your comment) var id = node[0].id; (getting id from node[0]) is ok, then if(id == dryerList[i].id) looks wrong, since you just (re-)defined id to be the value of node[0]'s id.
Actually I would not use 'id' as a var-name (in this case).
So what if you did: var idc = node[0].getAttribute("id");
and then: if(idc === dryerList[i].getAttribute("id"))
UPDATE 5: You still have some errors by the way:
You forgot a semi-colon to close the alert in:
if(dryerList.length == 0) {
alert('ERROR: No fleet data received.')
}
You should use '===' to compare with '0' on line 2 and 14
naturally in real life you would define function customBinMenu(node) before it was used in your document.ready function.
Fixed by swapping code order.
The same goes for this document.ready function where you used var dryerList before it was defined.
Fixed by: var dryerList = dryerList || []; if(dryerList.length === 0){//ajax & json code}
Could you please confirm if this fiddle, which is now valid javascript, represents your intended baseline-code that still results in your problem of the 'id'-attribute being 'undefined' in dryerList's node-collection (since the code you posted contained some simple errors that are fixed in this jsfiddle, excluding the things mentioned in update 1, since you commented that this is not the problem) ?
May I ask (since you start at document.ready), why do you (still) check if dryerList already exists?
May I ask if you could update that corrected fiddle with some demo-data for us to toy around with?

Print plugin phonegap

Hi i'm quite confused on some parts of the Print Plugin or the Phonegap plugin. See i was able to implement the code even created my own plugin but i was no returning values from objective-c (xcode) back to javascript so it was safe to say that it was easy to understand.
On this code:
https://github.com/phonegap/phonegap-plugins/blob/master/iPhone/PrintPlugin/PrintPlugin.js
On this block of code:
PrintPlugin.prototype.callbackMap = {};
PrintPlugin.prototype.callbackIdx = 0;
PrintPlugin.prototype.print = function(printHTML, success, fail, options) {
if (typeof printHTML != 'string'){
console.log("Print function requires an HTML string. Not an object");
return;
}
//var printHTML = "";
var dialogLeftPos = 0;
var dialogTopPos = 0;
if (options){
if (options.dialogOffset){
if (options.dialogOffset.left){
dialogLeftPos = options.dialogOffset.left;
if (isNaN(dialogLeftPos)){
dialogLeftPos = 0;
}
}
if (options.dialogOffset.top){
dialogTopPos = options.dialogOffset.top;
if (isNaN(dialogTopPos)){
dialogTopPos = 0;
}
}
}
}
var key = 'print' + this.callbackIdx++;
window.plugins.printPlugin.callbackMap[key] = {
success: function(result) {
delete window.plugins.printPlugin.callbackMap[key];
success(result);
},
fail: function(result) {
delete window.plugins.printPlugin.callbackMap[key];
fail(result);
},
};
var callbackPrefix = 'window.plugins.printPlugin.callbackMap.' + key;
return PhoneGap.exec("PrintPlugin.print", printHTML, callbackPrefix + '.success', callbackPrefix + '.fail', dialogLeftPos, dialogTopPos);
};
Especially this lines of code:
PrintPlugin.prototype.callbackMap = {};
PrintPlugin.prototype.callbackIdx = 0;
I'm confused by what that two lines of code does and why it is somehow important to incorporate or follow when you want to return values from xcode to javascript (NOTE: by me saying why it is somehow important to incorporate or follow when you want to return values from xcode to javascript i'm saying this based on what I've understood so far)
Can somebody explain how the two lines of code works and what are their purpose? Thank you.

Adding mutual friend count to each user in an array

I have an array of Facebook users (userList) and I want to store the number of mutual friends for each user in the array as a property (mfCount). I have checked that I am getting the correct number of mutual friends if I put in an individual user, but I'm not sure why I can't add this value to each user in the array?
function getfriends() {
FB.api('/me/friends', function(response) {
userList = userList.concat(response.data);
userCount = response.data.length;
for( i=0; i<response.data.length; i++) {
userId = response.data[i].id;
FB.api('/me/mutualfriends/'+userId+'/', function(response) {
userList[i].mfCount = response.data.length;
userCount--;
if(userCount === 0) { display_results();}
});
}
});
}
Have a look at the implementation below.
I've broken it out into multiple functions to separate each step.
When you're dealing with loops and callbacks, it becomes very important to keep track of what scope your anonymous functions are being defined in.
You can theoretically do it all in a one-liner like you were writing...
...but it gets very, very confusing as you go further and further into nested-callbacks.
One solution would be to make every variable inside each function 100% global, so that only i needs to have an enclosed reference. That's not really pretty, though.
Look through each function and take note of what parameters are going into the functions each step calls (or closures for callbacks). They're all needed (whether you separate them this way, or through closures in a one-liner or whatever).
The following worked just fine for me, inside of the Facebook developer sandbox (first time using the API).
The logs were for my benefit to see how the data was coming out, and to keep a basic stack-trace.
var userList = [],
userCount = 0;
function getfriends () {
//console.log("getFriends");
var url = "/me/friends";
FB.api(url, function (response) {
if (response.error && response.error.message) { return false; }
userList = userList.concat(response.data);
userCount = response.data.length;
compareAllFriends();
});
}
function compareAllFriends () {
//console.log("compareAllFriends");
var i = 0, l = userCount, userID;
for (; i < l; i += 1) {
userID = userList[i].id;
compareFriendsWith (i, userID);
}
}
function compareFriendsWith (i, id) {
//console.log("compareFriendsWith", i, id);
var path = "/me/mutualfriends/",
url = path + id + "/";
FB.api(url, (function (i) {
return function (response) {
//console.log(i, response);
var numFriends = (response.data) ? response.data.length : 0;
setMutualFriends(i, numFriends);
userCount -= 1;
//console.log(userCount);
if (userCount === 0) {
display_results();
//console.log("DISPLAYING");
}
};
}(i)));
}
function setMutualFriends (i, friendcount) { userList[i].mfCount = friendcount; }

Categories