Loop doesn't find a candidate even though a candidate exists? - javascript

I've recently started developing a discord bot (using the discord.js library). The bots purpose would be to find small servers in a game called ROBLOX.
I've already learned the ROBLOX api, and written most of the code.
When I type the command, the loop runs through all of the servers, but to my surprise, it doesn't find a suitable candidate.
This is a part of my code:
result = api.responseText
found = false
number = parseInt(args[0])
for (var i = 0; i < result.length; i++){
if(result[i].playing <= number) {
message.reply("found candidate")
found = true
} else {
if (i == result.length - 1) {
message.reply("no candidate found.")
}
}
}
https://i.stack.imgur.com/InEYM.png
The image shows roughly what the JSON looks like.
Any help would be greatly appreciated!

There are two main issues:
.responseText is likely a JSON-encoded string, not an object. This will need to be parsed into an object.
result.data is the array containing the server data (specific to the Roblox Web API), not result. There are three places where this will need to be updated.
These can be fixed like this:
result = JSON.parse(api.responseText)
found = false
number = parseInt(args[0])
for (var i = 0; i < result.data.length; i++){
if(result.data[i].playing <= number) {
message.reply("found candidate")
found = true
} else {
if (i == result.data.length - 1) {
message.reply("no candidate found.")
}
}
}
There could also stand to be some error checking as the API may return something else, for example, an error message. Also, the result sets are provided in a paged format so that might need to be handled as well. (Though it looks like the API is currently returning the full set of servers in one call.) I'll leave those for another question if necessary.

Try use
const filter = result.filter( (item) => item.playing <= number)
const found = filter.lenght > 0

Related

Discord.js Sorting through all text channels on a server

module.exports = {
name: 'cleartextchats',
description:
'clears desired amount of text chats created by the bot. usage: cleartextchats <amount>',
execute(message, args) {
var channelAmount = args[0];
console.log('executing cleartextchats');
for (var i = 0; i < channelAmount; i++) {
var textChat = message.guild.channels.cache
.filter((ch) => ch.type === 'text')
.find(
(targetch) => targetch.topic == 'volturnbot' && targetch.deleted == false
);
message.channel.send(`deleting ` + textChat.name);
textChat.delete();
}
},
};
The for loop does not advance past the first deleted channel.
I presume this is because the cache does not update unless I use a different/new message but the command is supposed to run from a message. I cant think of a way to implement this that would work for any amount of channels.
Currently whenever my bot creates text channels it updates the topic to include "volturnbot" so that it can delete its own channels without a category.
I presume this is because the cache does not update unless I use a different/new message but the command is supposed to run from a message.
I believe that assumption is correct. It is probably because the channel.deleted property is still false even after you delete the channel in the for loop, since there is probably a slight delay associated with channel deletion. There are two solutions I can think of:
A) Instead of using a for loop, you could perhaps use setInterval as a loop with a delay of 1000 milliseconds or so (might have to increase or decrease that depending on what works). You would need to create two new variables: one to contain the setInterval loop, and another to keep track of how many iterations of the loop have occurred. When the iterations variable is equal to the channelAmount variable, that would be when you use return clearInterval(intervalVariable) where intervalVariable is the variable containing the loop. (This is assuming a timeout gives the cache enough time to update after the channel is deleted).
B) Loop through the cached text channels instead of fetching one channel from the cache each time. Example:
var channelAmount = Number(args[0]);
console.log("executing cleartextchats")
var channels = message.guild.channels.cache.filter(ch => ch.deleted == false && ch.type === 'text' && ch.topic && ch.topic.match("volturnbot"));
var iterations = 0;
for (var textChat of channels) {
iterations++;
message.channel.send(`deleting ` + textChat.name)
textChat.delete()
if (iterations == channelAmount) break;
}
Honestly I prefer option B, and I am positive option B would work much better in terms of what you want to achieve here. You also mentioned that the channel topic "includes" the phrase "volturnbot", I'm not sure if that indicates the description could be more than exactly "volturnbot" so I made option B use topic.match("volturnbot") for that reason.
Because guild.channels.cache returns a collection, you have to use .array()
also, instead of finding a single channel and then searching through the same cache, put all channels in a single array and loop through that.
var channelAmount = Number(args[0]);
var textChats = message.guild.channels.cache
.filter((ch) => ch.type === 'text' && ch.topic === 'volturnbot')
.array()
for (var i = 0; i <= channelAmount; i++) {
message.channel.send("deleting " + textChats[i].name)
//textChats[i].delete()
}

recursive function with multiple parameters in javascript/reactjs

I am writing some react code, in which I build a list of diagnoses. These diagnoses are build dynamically, so one can add sub diagnoses and attributes to them by clicking them. Therefore, I want to know where some potential diagnosis is placed in my list, and therefore when creating a new diagnosis, I give it a path as an attribute, which I can then use to navigate to it from the list.
I want to be able to set an attribute 'showRequirements' to a given diagnosis, and for this I implement the following two functions:
onClick = (path) => () => {
let currentDiagnosis = this.state[path[0].type][parseInt(path[0].key, 10)];
if (path.length > 1) {
this.showRequirementsFromPath(path, currentDiagnosis.algorithm.children, 1)
}
else {
currentDiagnosis.showRequirements = !currentDiagnosis.showRequirements;
}
this.setState({
[this.state[path[0].type][parseInt(path[0].key, 10)]]: currentDiagnosis,
})
}
showRequirementsFromPath = (path, diagnosis, counter) => {
if (counter < path.length) {
diagnosis[path[counter].key].showRequirements = true;
this.showRequirementsFromPath(path, diagnosis[path[counter].key], counter + 1);
}
else {
diagnosis.showRequirements = !diagnosis.showRequirements;
}
}
The onClick works when the path has length 1, so I believe the problem is in the showRequirementsFromPath function. If I run this with a path of length > 1, the app crashes, and I get the error message 'Too much recursion'. However, if I delete the diagnosis.showRequirements = !diagnosis.showRequirements from the else in showRequirementsFromPath, the app doesn't crash, and it does everything perfectly besides setting the attribute showRequirements.
This is my first post in here, so please tell if I'm breaking some guidelines/what I can do better in future posts.
Thanks in advance!
Edit: As asked, the type of path[0].key is String. Note that path[counter].key is an integer when counter > 0.
Update: I am so sorry, I just found out that the problem was elsewhere in the code. I believe the code I have posted is actually correct.

For loop exiting early while updating modified or new records in datatable.net table

I'm using the Datatables jQuery plugin. The table is pulling the data from an AJAX source (a SQL Server query, processed by ASP.NET into a JSON object). I want to create a live view of the table so that changes appear in real time. But rather than reloading the entire table every few seconds with fnReloadAjax() (which, from experience, has proven to be quite burdensome on the browser) I'm only updating the records that are new or modified, using fnAddData() and fnUpdate().
After getting a JSON object of just the new or modified records, here's my code to process the object.
var newData = updatedDataJSON.aaData;
if (newData[0] != null) {
for (i = 0; i < newData.length; i++) { //Loop through each object
if (newData[i].bNewCase === true) { //Process new cases
oTable.fnAddData(newData[i]);
} else { //Process modified cases
var tableArray = oTable.fnGetData();
var index;
var found = false;
var serial = newData[i].serial;
var dataObject = newData[i];
//First gotta find the index in the main table for
// the record that has been modified. This is done
// by matching the serial number of the newData
// object to the original aData set:
for (ii = 0; ii < tableArray.length; ii++) {
var value = tableArray[ii]['serial'];
value = value.replace(/<\/?[^>]+(>|$)/g, "");
if (value === serial) {
index = ii;
found = true;
}
}
if (found) {
oTable.fnUpdate(dataObject, index);
console.log('Updated ' + newData[i].serial);
}
}
}
}
My problem is that even though the newData.length property of the first for loop could be greater than 1, the for loop exits early (after one iteration). I added the console.log statement at the end and it started passing errors saying that newData[i].serial was undefined. This makes me think that the entire newData array was destroyed or something...
I'm really hoping that I've just made a stupid mistake (though I've checked and checked and checked some more but can't find one). Maybe there's something that I'm overlooking. If anyone has any advice, it would be greatly appreciated.
Credit goes to #elclarnrs for the solution, posted above in the comments. The solution was declaring the values of i and ii in the scope of the function. That got everything working smoothly. Good to know for future reference.

Why is this JavaScript code entering a for loop after the boolean expression is no longer true?

Edit: This turned out to be a case of Firebug incorrectly stepping through the code, while my bug was elsewhere. A coworker who has seen similar behavior suggested that clearing the browser cache regularly can help.
I have this simple function that splits a comma-delimited string and uses the tokens to populate a Dojo data store:
UpdateFileNames: function(names) {
var fileNames = names.split(",");
var fileNameData = [];
for (var i = 0; i < fileNames.length; i++) {
fileNameData.push({ name: fileNames[i], id: fileNames[i] });
}
this.fileListStore = new dojo.store.Memory({ data: fileNameData });
}
I'm stepping through this code in Firebug and can't believe what I'm seeing. Everything is happy until i == fileNames.length, at which point the loop is entered again and fileNames[i] is invalid.
What's going on here?
Does names end with a comma? If so, fileNames[fileNames.length - 1] == ''.
proof of concept
Have you tryed to to loop one less?
for (var i = 0; i < fileNames.length-1; i++) {
fileNameData.push({ name: fileNames[i], id: fileNames[i] });
}
If that works you need to look at how you generate your list (names). So that you dont get blank elements at the end.

Help refactor a small piece of Javascript code which identifies user's referrer source

I've written the following small piece of javascript (Based on the excellent parseURI function) to identify where the user originated from. I am new to Javascript, and although the code below works, was wondering if there is a more efficient method of achieving this same result?
try {
var path = parseUri(window.location).path;
var host = parseUri(document.referrer).host;
if (host == '') {
alert('no referrer');
}
else if (host.search(/google/) != -1 || host.search(/bing/) != -1 || host.search(/yahoo/) != -1) {
alert('Search Engine');
}
else {
alert('other');
}
}
catch(err) {}
You can simplify the host check using alternative searches:
else if (host.search(/google|bing|yahoo/) != -1 {
I'd also be tempted to test document referrer before extracting the host for your "no referrer" error.
(I've not tested this).
I end up defining a function called set in a lot of my projects. It looks like this:
function set() {
var result = {};
for (var i = 0; i < arguments.length; i++)
result[arguments[i]] = true;
return result;
}
Once you've got the portion of the hostname that you're looking for...
// low-fi way to grab the domain name without a regex; this assumes that the
// value before the final "." is the name that you want, so this doesn't work
// with .co.uk domains, for example
var domain = parseUri(document.referrer).host.split(".").slice(-2, 1)[0];
...you can elegantly test your result against a list using JavaScript's in operator and the set function we defined above:
if (domain in set("google", "bing", "yahoo"))
// do stuff
More info:
http://laurens.vd.oever.nl/weblog/items2005/setsinjavascript/

Categories