recursive function with multiple parameters in javascript/reactjs - javascript

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.

Related

Reduce the total number of "break" and "continue" statements in this loop to use one at most in Javascript

I am refactoring code written by another developer. On SonarQube, the error thrown is that I need to "Reduce the total number of "break" and "continue" statements in this loop to use one at most".
I'm trying to refactor the code so that it logically makes sense but without the use of continue. As a problem solver, what's the best approach to refactor the code and still maintain the same logic?
See sample code below.
// once we reach the first item that was created before the user's itemUpdatedAt,
// we know that the user has accepted that item
const userReport = (user, items) => {
for (const item of items) {
if (user.itemUpdatedAt < item.createdAt) {
continue;
}
reportItem['Accepted date'] = intl.format(
new Date(user.itemUpdatedAt)
);
reportItem['Version accepted'] = item
? intl.format(new Date(item.createdAt))
: null;
break;
}
return reportItem;
}
I have tried to add the break code where the continue statement is and reverse the if logic. But I'm uncertain that it produces the same logic that needs to be achieved.
Per Pointy's comment, you can use Array#find to determine if any of the items have been "accepted".
const setAcceptanceMetadata = (reportItem, user, items) => {
const firstAcceptedItem = items.find((item) =>
user.itemUpdatedAt >= item.createdAt)
if(firstAcceptedItem) {
reportItem['Accepted date'] =
intl.format(new Date(user.itemUpdatedAt));
reportItem['Version accepted'] =
item ? intl.format(new Date(item.createdAt)) : null;
}
return reportItem;
}

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

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

Is there a way to discontinue a forEach loop once you reach a certain key:value pair in the array?

I'm still very new to Javascript and software development in general as this is my first job, and right now I am debugging a critical defect related to validation.
I have a scenario where I have an array, and within that array each product has an attribute labeled Unit Volume with a code of ASC_Sales_Volume.
The current defect is that when a user configures more than one product, but leaves one with a unit volume of zero, the user is still allowed to navigate forward to the next page despite there being a validation check in place to prevent this. The condition that would block the user from moving forward is if $scope.bpTree.response.errorCheckVolume = true.
WIth what is currently written by the former dev, the forEach loop keeps changing the $scope.bpTree.response.errorCheckVolume back to false when the user clicks on the button to fire the functions.
The Next button itself is an OOTB component that I cannot modify directly.
What would be the next best step in troubleshooting this? Basically, when the forEach evaluates a product with a Unit Volume value of 0, the $scope.bpTree.response.errorCheckVolume should always equal true regardless of subsequent Unit Volume values, until the user changes the Unit Volume value for that particular product and then clicks the Next button and the logic fires again. If the error records (as in, any product in the array with a Unit Volume of 0) are greater than zero, then errorCheckVolume should always evaluate to true and the user should be blocked from progressing.
Would I use something similar to Array.prototype.some()? I've just been staring at my screen for hours trying different things to no avail.
Here is the code snippet:
function validateAllPlansUnitVolume(triggeredFromNextBtn) {
var coveragesData = $scope.sortedCoverages[$scope.childProduct.instanceKey || $scope.childProduct.productName || $scope.childProduct.Name],
errorRecords = checkForInvalidPlans(coveragesData, triggeredFromNextBtn);
if(errorRecords > 0) {
$scope.bpTree.response.errorCheckVolume = true;
}
else {
$scope.bpTree.response.errorCheckVolume = false;
}
}
function checkForInvalidPlans(coveragesData, triggeredFromNextBtn){
if(!!coveragesData && coveragesData.length) {
coveragesData.forEach(function(product){
if(!!product.attributeCategories){
unitVolumeAttrNodes = product.attributeCategories.records[0].productAttributes.records.filter((ele) => ele.code == "ASC_Sales_Volume");
if(!!unitVolumeAttrNodes && unitVolumeAttrNodes.length){
unitVolumeAttrNodes.forEach(function(attrNode){
if(unitVolumeAttrNodes[0].userValues === 0){
$scope.bpTree.response.errorCheckVolume = true;
product.unitVolumeErr = true;
product.unitVolumeErrMsg = "Unit Volume cannot be zero. Please update before proceeding to the next screen";
}
else {
product.unitVolumeErrMsg = "";
product.unitVolumeErr = false;
$scope.bpTree.response.errorCheckVolume = false;
}
});
}
}
});
return coveragesData.filter((ele) => ele.unitVolumeErr).length;
}
return 0;
}
i will suggest you should use Array.prototype.some() for this purpose. its not good practice to use foreach loop then break it on condition.
Js has different array's method for different work. i think for different requirement they created built methods otherwise we can use one method for all work,
Visit this page select array method that fit in your requirement
You can't do that with .forEach, but you can with .some:
['a', 'b', 'c'].some((element, index) => {
console.log(element);
return element == 'b' || index == 1;
});
This code logs each element of the array, discontinuing the loop after it reaches 'b' or index 1.

Text Input Value sometimes does't change even though it thows no errors, and seems to work

So I have a simple program to change the value of an input field every time you blur it. It logs the already used values in an array, an I use that array to check if it's been used. It practically works as intended, but after a few tries it will return true and logs, yet the value wont change.
Updated Code:
var dftvalue = ['Freddy the Grocer', 'Jack the Fiddler', 'Cane the Sheep Herder', 'Arnold the Fish Monger', 'Luke the Car Salesman', 'Josh the Tailor', 'Carol the Baker', 'Tiara the Nacho Vendor', 'example#email.com', 'Your message here.'];
var logused = new Array(); //create new array to log the used indexs
function setdftvalue() {
var newval = dftvalue[Math.floor(Math.random() * 7)];
if (logused.indexOf(newval) == -1) {
this.value=newval;
logused.push(newval);
console.log(logused);
} else if (logused.indexOf(newval) >= 0) {
setdftvalue();
}
if (logused.length == 8) {
for (i=0; i<=7; i++){
logused.pop();
}
}
}
document.getElementById('formname').onblur=setdftvalue;
JSFIDDLE
https://jsfiddle.net/e5pdz37e/8/
Your approach is unnecessarily complicated. At a high level I would recommend an approach that's more like this:
function setdftvalue() {
if (index === (dftvalue.length - 1)) {
// Shuffle your names array
index = -1;
}
input.value = dftvalue[++index];
}
This way you won't need to use any recursion and make unnecessary function calls. And the only time you'll need to randomize is when you've used up all of your available names.
Here's a working example: http://jsfiddle.net/bvaughn/163mqdeL/
Original answer
After a few invocations, your function will fill up the logused Array, at which point calling it again will do nothing. Actually, worse than nothing - it will recursively call itself without end.

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.

Categories