I have 2 javascript requests that give back results in an array of objects.
The first object looks like this:
[Object {user_id="6", meta_value="5", user_nicename="richbai90", more...},
Object {user_id="7", meta_value="1", user_nicename="testing123", more...}]
the 2nd looks like this
[Object { usr="6", score="1 / 1", quiz_id="1"},
Object { usr="7", score="1 / 1", quiz_id="1"},
Object { usr="7", score="1/5", quiz_id="3"}]
Array 2 is the details of array one
What I need is a way to relate these together in javascript so that I can put the information from object 2 in the document where it needs to correspond with the information from object one. The easiest way I could think to do this would be to combine the arrays where the user ids were the same but this appears to be more difficult then I first thought. Here was my initial approach:
$.post(AjaxRequest.ajaxurl, {
action: "get_data"
})
.done(function (json) {
console.log(json);
var data = json;
for (var i = 0; i < json.length; i++) {
if (AjaxRequest.user_ID == json[i].user_id && json[i].Quizes == "1") {
$("#result_list").append("you have taken " + json[i].Quizes + " quiz");
} else if (AjaxRequest.user_id == json[i].user_id && json[i].Quizes != "1") {
$("#result_list").append("you have taken " + json[i].Quizes + " quizzes");
} else {
$("#result_list").append(json[i].user_nicename + " has taken " + json[i].Quizes + " quizzes" + "<br>");
}
}
getDetails(json);
})
.fail(function (jqxhr, textStatus, error) {
var err = textStatus + ', ' + error;
console.log('1st Request Failed: ' + err);
});
function getDetails(data) {
$.post(AjaxRequest.ajaxurl, {
action: "get_details"
})
.done(function (details) {
console.log(data);
console.log(details);
for (var i = 0; i < data.length; i++) {
for (var i2 = 0; i2 < details.length; i++) {
while (details[i2].usr == data[i].user_id) {
console.log(details[i2]);
break;
}
}
}
$("#loading").fadeOut('fast', function () {
$("#result_list").fadeIn('fast');
});
})
.fail(function (jqxhr, textStatus, error) {
var err = textStatus + ', ' + error;
console.log('2nd Request Failed: ' + err);
});
}
In this code block is where the work is happening
for (var i = 0; i < data.length; i++) {
for (var i2 = 0; i2 < details.length; i++) {
while (details[i2].usr == data[i].user_id) {
console.log(details[i2]);
break;
}
}
}
The issue is that once the while loop breaks it doesn't seem to go to the next itteration of the for loop as I would have expected instead data[i] gets undefined. If I remove the break; then data[i] is always == details[i2] and thus crashes the browser.
Maybe I'm making it harder than it needs to be?
You could try using a 2-dimensional array.
Related
I have a client-side web-application that takes a csv-file, parses it into various data types, searches for something specific, and displays a table with the answer on the screen. The search function returning a null string. This occurs because its search parameter, returned by a callback function and put into lib, returns null.
I'm fairly certain this is a callback issue, but I've messed around with the order so much I'm not sure what goes where anymore in my html...A second set of eyes would be appreciated.
The desired series of events
fileToArray() gives us an array
search() looks in the array for its specified item and returns a csv-format string containing what it found
displayTable takes that csv-format string and outputs it to the desired location
The Code
// jQuery call to fetch the client-side csv file - this works when called by itself.
const fileToArray = () => {
console.log("fileToArray started.");
$.get({
url: CSV_LOCATION,
dataType: "text",
success: function (result) {
console.log("splitting result by newline...");
let csvLines = result.split("\n");
console.log("split successful. generating array into retval ...");
let retval = [];
for (let i = 0; i < csvLines.length; i++) {
// [0][0] is number [0][1] is class, [0][2] is unit, [0][3] is lesson
retval[i] = csvLines[i].split(",");
}
console.log("success! Returning retval.");
return retval;
// callback(result);
// return result;
},
failure: function (xhr, status, error) {
console.log("ERROR: fileToString(): " + xhr + " ||| " + status + " ||| " + error);
alert("ERROR: fileToString(): " + xhr + " ||| " + status + " ||| " + error);
}
})
};
// PRECONDITION: form is #search-params in index.js
// > lib is the result of fileToArray()
// POSTCONDITION: result is a csv-format string to be passed to displayTable() in index.js
const search = (form, callback) => {
console.log("search called...");
// vvvvv The probable root of the problem vvvvv //
let lib = callback;
console.log(lib.length + " is lib's length.");
let result = "";
console.log("search nested for loop called...");
for (let i = 0; i < lib.length; i++) {
// check class
console.log("checking class " + form.class.value + "...");
if (lib[i][1] === form.class.value) {
// check unit
console.log("checking unit " + form.unit.value + "...");
if (Number(lib[i][2]) === Number(form.unit.value)) {
console.log("adding to result...");
result += lib[i] + "\n";
}
}
}
console.log("search success! result: " + result.length + " characters");
console.log(result);
return result;
};
<!-- I'm almost 100% certain I've messed up the callback in this button,
but I still don't quite understand how... I've played with
displayTable(fileToArray(search(...))), but I don't quite know how it should go -->
<button class="btn btn-primary"
onclick="displayTable(search(document.getElementById('search-params'), fileToArray), $('#card-display'))">
Submit
</button>
What I've tried
I have looked to the following sites for inspiration (none have helped):
JavaScript is Sexy
JavaScript: Passing parameters to a callback function
JavaScript Callback Functions
Passing arguments to callback functions
In Summary
It's painfully obvious I still don't understand callbacks fully. Any help would be appreciated.
You could use async / await
const displayTable = async () => {
let arrayFromFile = await fileToArray(); // fileToArray executes and assigns the returned value when it completes
let searchedData = search(form, arrayFromFile);
// Display the table
};
Thanks to #kapantzak for the inspiration!! Turns out, I was using callbacks horribly bass-ackwards. According to this, the old-school async style is something akin to
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
So, the relevant code now looks like this:
const fileToArray = (callback) => {
// console.log("fileToArray started.");
$.get({
url: CSV_LOCATION,
dataType: "text",
success: function (result) {
let csvLines = result.split("\n");
let retVal = [];
for (let i = 0; i < csvLines.length; i++) {
// [0][0] is number [0][1] is class, [0][2] is unit, [0][3] is lesson
retVal[i] = csvLines[i].split(",");
}
callback(retVal);
},
failure: function (xhr, status, error) {
console.log("ERROR: fileToString(): " + xhr + " ||| " + status + " ||| " + error);
alert("ERROR: fileToString(): " + xhr + " ||| " + status + " ||| " + error);
}
})
};
// =======
const search = (form, lib, callback) => {
let result = "";
let formClass = form.class.value.toLowerCase();
let formUnit = form.unit.value.toLowerCase();
let formLesson = form.lesson.value.toLowerCase();
for (let i = 0; i < lib.length; i++) {
// check class
if (lib[i][1].toLowerCase() === formClass) {
// check unit
if (Number(lib[i][2].toLowerCase()) === Number(formUnit)) {
result += lib[i] + "\n";
}
}
}
console.log(result);
callback(result);
};
<button class="btn btn-primary"
onclick="fileToArray(function(result) {
search(document.getElementById('search-params'), result, function(newResult) {
displayTable(newResult, $('#card-display'));
});
});">
Submit
</button>
This righted the wrongs and caused my search and display to function properly.
I made a script in Javascript (for phantomjs) to automate tests in a website.
I would like to count the errors encountered.
With this script, I get the error : "nbe variable unknown".
I understand that try... catch works in a specific way but I don't know what to do.
How can I modify my script to make it work?
Thank you
var nbe = 0;
var err = 0;
function next_page() {
page.evaluate(function() {
try {
document.querySelector('input[name="cc"]').click();
} catch (e) {
nbe++;
console.log('Error');
err = 1;
}
});
var k = i + 1 - nbe;
if (err == 0) console.log('Test ' + k + ' done');
i++;
if (i < links.length) setTimeout(handle_page, 1500);
else {
console.log('Errors : ' + nbe);
phantom.exit();
}
}
You could do something like the following:
var nbe = 0;
var err = 0;
var errors = [];
function next_page() {
page.evaluate(function() {
try {
document.querySelector('input[name="cc"]').click();
} catch (e) {
nbe++;
console.log('Error');
err = 1;
errors.push(e);
}
});
var k = i + 1 - nbe;
if (err === 0) {
console.log('Test ' + k + ' done')
};
i++;
if (i < links.length) {
setTimeout(handle_page, 1500);
} else {
for(var j = 0; j < errors.length; j++){
console.log(errors[j]);
}
phantom.exit();
}
}
I have a function which compares 2 array values. As soon as a mismatch value is found the execution stops , but
i want to only when all comparison have been done and if error has been found. There is OnLogError in testcomplete but
do not know how to use it
function compare() {
for (var i = 0; i < arrActualIntendedVal.length; i++) {
if (val1[i] != val2[i]) {
Log.Error("Value " + val1[intArrIndex] + " do not match to Actual Value " +
val2[intArrIndex]);
Runner.Stop(0);
}
}
return true;
}
You just need to "remember" that there was an error and post the corresponding info after your loop is finished.
function compare() {
var errors = new Array();
for (var i = 0; i < arrActualIntendedVal.length; i++) {
if (val1[i] != val2[i]) {
errors.push("Value " + val1[intArrIndex] + " do not match to Actual Value " + val2[intArrIndex]);
}
}
if (errors.length > 0) {
Log.Error("Error when comparing arrays", errors.join("\r\n"));
Runner.Stop();
}
return true;
}
I have a problem with a many-to-many relationship on parse.com.
First of all I have two classes (Groups and Users) and a class (UserInGroup) to realize the m-to-m relationship.
Class structure:
Groups:
objectId(string) -- GroupName(string) -- ...
Users:
objectId(string) -- UserName(string) -- ...
UserInGroup:
objectId(string) -- Group(Pointer< Groups>) -- User(Pointer< Users>)
How can I get the UserName of alle Users who are in a specificGroup (I know the objectId of the Group).
I trieds this
function getUserNameOfGroup(GroupID){
var UserInGroup = Parse.Object.extend("UserInGroup");
var query = new Parse.Query(UserInGroup);
var usersArray = [];
var Groups = Parse.Object.extend("Groups");
var collectionQuery = new Parse.Query(Groups);
collectionQuery.get(GroupID, {
success: function(group) {
query.equalTo("Group", group);
var usersArray = [];
query.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
var object = results[i];
var user = object.get("User");
user.fetch({
success: function(tempUser) {
usersArray.push(user.get("UserName"));
}
}).done(function() {
if(results.length == usersArray.length){
//do something with the array
}
});
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
},
error: function(object, error) {
alert("Error: " + error.code + " " + error.message);
}
});
}
For testing I put
if(results.length == usersArray.length){
for (var i = 0; i < usersArray.length; i++) {
alert(usersArray[i]);
}
}
in the done section. The first entry of usersArray is always "undefinde".
What am I doing wrong? Or rather, is there an easier, more beautiful and faster way to get the array? I'm sure there's a better solution ;).
thanks for help.
No need to do an additional query on the UserInGroup's User attribute. Those can be fetched eagerly using include(), as follows:
// return a promise that is fulfilled with an array of users belonging to
// the Group with id groupId
function usersInGroup(groupId) {
var groupQuery = new Parse.Query("Groups");
return groupQuery.get(groupId).then(function(group) {
var userInGroupQuery = new Parse.Query("UserInGroup");
userInGroupQuery.equalTo("Group", group);
// eagerly fetch the related user...
userInGroupQuery.include("User");
return userInGroupQuery.find();
}).then(function(results) {
var users = [];
for (var i = 0; i < results.length; i++) {
var object = results[i];
users.push(object.get("User"));
}
return users;
});
}
Call it like this:
usersInGroup(someGroupId).then(function(users) {
for (var i = 0; i < users.length; i++) {
var user = users[i];
console.log(user.getUserName()); // or get("username")
}
}, function(error) {
console.log(error);
});
Due to the callback-invoking nature of getGamesByPlayerId (which happens to be an Ajax call), I can't seem to figure out how to eliminate the duplicate code in the following:
// Load the player's games.
gc.api.getGamesByPlayerId(gc.game.player.id, gc.game.player.access_token, function(data) {
if(data.status_code === 401) {
// Call may have failed due to being called too fast. Retry...
gc.api.getGamesByPlayerId(gc.game.player.id, gc.game.player.access_token, function(data) {
if(data.status_code === 401) {
// Call may have failed due to being called too fast. Retry...
gc.api.getGamesByPlayerId(gc.game.player.id, gc.game.player.access_token, function(data) {
if(data.status_code === 401) {
// Call may have failed due to being called too fast. Retry...
gc.api.getGamesByPlayerId(gc.game.player.id, gc.game.player.access_token, function(data) {
if(data.status_code === 401) {
// OK. It's safe to assume the server is current, and that
// we truly are not authorized to do this.
alert("You are not authorized.");
} else {
// Add games to HTML.
for( var i = 0; i < data.length; i++ ) {
var html = '<li>' + data[i].id + '</li>';
$('#games').append(html);
}
}
});
} else {
// Add games to HTML.
for( var i = 0; i < data.length; i++ ) {
var html = '<li>' + data[i].id + '</li>';
$('#games').append(html);
}
}
});
} else {
// Add games to HTML.
for( var i = 0; i < data.length; i++ ) {
var html = '<li>' + data[i].id + '</li>';
$('#games').append(html);
}
}
});
} else {
// Add games to HTML.
for( var i = 0; i < data.length; i++ ) {
var html = '<li>' + data[i].id + '</li>';
$('#games').append(html);
}
}
});
Normally, I would think to use a for-loop, but that will not work because I don't want to fire off the Ajax calls in quick succession. I want the retry to fire only if the preceding call fails.
Ignoring the circumstances for which you would need to make the same request multiple times in a row, you could probably accomplish this with the use of a recursive function. For example, something like:
loadPlayerGames(4);
function loadPlayerGames(triesLeft) {
gc.api.getGamesByPlayerId(gc.game.player.id, gc.game.player.access_token, function(data) {
if(data.status_code !== 401) {
// Add games to HTML.
for( var i = 0; i < data.length; i++ ) {
var html = '<li>' + data[i].id + '</li>';
$('#games').append(html);
}
} else if(triesLeft <= 0) {
// OK. It's safe to assume the server is current, and that
// we truly are not authorized to do this.
alert("You are not authorized.");
} else {
// Call may have failed due to being called too fast. Retry...
loadPlayerGames(triesLeft - 1);
}
});
}