Best practice- when return in JS function - javascript

it happened to me multiple times that I face situation where I wasnt sure how I should stop function when I know result.
Just for example lets imagine we have object category which looks like:
{
"name": "CategoryName",
"subCategoriess": [{
"name": "Sub1",
"value": true
}, {
"name": "Sub2",
"value": false
}]
}
and we want to iterate over it until we found value === true
Clasic option (let's call it: Option 1)which i found in most documentation is:
function myFunction(category){
var ret = false;
for (var i=0; i <category.subCategoriess.length; i++)
{
ret = category.subCategories[i].value;
if (ret)
break;
}
return ret;
}
I alternativly saw something like this (let's call it: Option 2):
function myFunction(category){
for (var i=0; i <category.subCategoriess.length; i++)
{
if (category.subCategories[i].value) return true;
}
return false;
}
So let's compare this two options, i consider my self as junior so I may be wrong really easy so please do not hesitate to tell me that
Option 1 - Advantage of this option is that even if your code is more complicated you always know which return actualy "return" the function and I expect it may be easier for debug for many of us.
Option 2 - Advantage of this option is that it should use less performance becuase it doesnt need aditional variabile (ret) + we basicly avoid atleast one step where we break a for loop and going to actual return statment
In this particular example the differenc between each option is something really close to 0. But code can get more complicated so may I ask you guys what option is best practice (or if I miss something please add your option) and why it should be used like this?

There is more then just two options and it comes down to this:
Which do you (or your team) find is more comprehensible with the lowest cognitive load to read?
Some find procedural code easier. Some attempt to lower temp variables by returning early. Still some find a functional approach better:
return subject.collection.some(function (item) {
return !!item.value;
});
So it comes down to your team and your own cognitive preferences. The true take away is be consistent in your project. If you start with one style and find you like a new style note this in the README and maybe convert old styles as you see them.

The only answer to this question is: it depends (on code complexity)
Generally speaking, multiple return statement could make debugging a bit harder, but sometimes it is worth it.
In your example I'd probably go for the second option if I'd be sure that this method would only return true/false value.
But if I'd like to extend it's functionality in the future (fetch the record which fulfills the condition or get all the values that fulfill it) it may be easier to go for the first option.

I'd say option 2 because you don't need any intermediary variable to store the result, and the intent is clearer since you return immediately as soon as you can.
You can also improve legibility and performance (a bit) by storing each item as you iterate.
for (var i = 0, item; item = category.subCategoriess[i]; i++) {
if (item.value) return true;
}
return false;

How about Option 3?
Using Array.some gives you the best of both worlds as it doesn't require a break or multiple return statements.
var category = {
"name": "CategoryName",
"subCategoriess": [{
"name": "Sub1",
"value": true
}, {
"name": "Sub2",
"value": false
}]
}
function myFunction(category){
var result = category.subCategoriess.some(function(subCategory) {
return subCategory.value;
});
return result;
}
console.log(myFunction(category))

Related

Javascript - Randomly choosing items within an object, but not choosing excluded items

I am working on a project where I need to contain a lot of functions within an object so that I can access them with variables that combine in different ways to access different ones. I haven't written this in my main project yet, just tested it as a method and I'll put that test code below to hopefully better explain what I'm talking about. There will be a lot of functions in this object, and the aim is to have one of them randomly run when a button is pressed.
A key part of what I'm trying to make is that the user can exclude whichever functions they want from being randomly selected, and this is what is causing me some problems. I read through a lot of solutions for other problems, but all of them are just a little off (or at least I don't know how to apply them to this just yet). The first thing I tried was using an array and, depending on what option the user ticks, excluding or adding items to it and then randomly selecting from within that to get the random number, but I'm worried that an array of around 300 items would just slow things down or cause more problems than is really useful so I stepped away from this. I looked up how I could exclude numbers from the random generation too, but this tended to use for loops and only worked for a few numbers being excluded - after that I imagine that would cause problems too. I looked into excluding ranges of numbers from random generation as well, but the items that users exclude aren't guaranteed to be next to each other in the object, so that causes problems.
The plan in my head was to add a number at the end of the object key; the random number generator would choose from all non-excluded numbers (exclusion based on other parts of the key, such as excluding all 'salad' options in the test below), and then that number, along with the two or three other variables that make up the key bring us to an actual thing. So, in the theme of the below, "bacon" + "burger" + "lettuce" + "17" might be chosen based on the options the user chooses and the random number respectively and might lead to the 17th baconburgerlettuce based alert - I'm not making some kind of food shouting app, by the way, this really is just a demo...
This is... Hopefully less complicated than it sounds. I imagine I have explained it awfully, so if I can even just clarify something please let me know. Alternatively, if nesting functions inside an object for this purpose is straight up dumb, tell me! I want to learn the best way of doing things, rather than fumble through something ugly and inelegant!
var food;
var type;
var func = {
cheeseburger: function() {alert("This is CHEESE BURGER");},
baconburger: function() {alert("BACON BURGER reigns supreme!");},
cheesesalad: function() {alert("CHEESE SALAD hahahaha");},
baconsalad: function() {alert("BACON SALAD! That's right!");}
};
$(".cheese").click(function() {
food = "cheese";
$(".test").html(food);
});
$(".bacon").click(function() {
food = "bacon";
$(".test").html(food);
});
$(".burger").click(function() {
type = "burger";
$(".test2").html(type);
});
$(".salad").click(function() {
type = "salad";
$(".test2").html(type);
});
$(".go").click(function() {
func [food + type]();
});
const funcs = [
v => alert(v),
v => console.log(v),
v => alert( v + 1)
];
const excluded = new Set();
//to exclude a function
excluded.add(2);
function random(...args){
do {
var i = Math.floor( Math.random() * funcs.length );
} while( excluded.has( i ) );
funcs[i](...args);
}
//test it
random("test");
First of all you need some property to enable or disable functions.
var func = {
cheeseburger: {f: function() {...}, enabled: true},
baconburger: {f: function() {...}, enabled: true},
cheesesalad: {f: function() {...}, enabled: true},
baconsalad: {f: function() {...}, enabled: true},
...
};
Then a function to pick random one ignoring disabled ones.
Here arises first problem: If you just randomly pick one until you find one
which is not disabled, as the amount of disabled ones grows, the it will take
longer to find enabled one.
The easier solution is to use a cache like:
var cache;
function updateCache(){ // Using function we can update it when needed.
cache = Object.keys(func).filter(k=>func[k].enabled);
};
updateCache();
Then you need enable / disable methods too:
function enable(fn) {
func[fn].enabled = true;
updateCache();
};
function disable(fn) {
func[fn].enabled = false;
updateCache();
};
And, finally you just need to pick function keys from the cache:
function callRandomFn(){
var k = Math.floor(Math.random() * cache.length);
return fn[cache[k]]();
};

Using if/else vs. switch

I'm wondering if it would be better to use a switch statement in my specific case.
I'm writing an Alexa Custom Skill, and I need to "redirect" to the appropriate intent depending on the available information (aka slots). Below is what I have currently (using if/else):
if (_type === "IntentRequest") {
this.handler.state = states.START;
if (_slots.indexOf("address") != -1) {
this.emitWithState("GoingToAddressIntent");
} else if (_slots.indexOf("place") != -1) {
this.emitWithState("GoingToPlaceIntent");
} else if (_slots.indexOf("type") != -1) {
this.emitWithState("GoingToTypeIntent");
} else if (_slots.indexOf("purpose") != -1) {
this.emitWithState("GoingToPurposeIntent");
} else {
this.emit("Unhandled");
}
}
I expect _slots to be an array of any permutations of the four elements, [ "address", "place", "type", "purpose" ]. Therefore, it could be anything from [ "address" ] to [ "place", "purpose" ] to etc. etc., but always in the same order (e.g. [ "purpose", "address" ] would never happen).
The order of the comparisons matters because there is a "hierarchy" of information; so if the "address" slot is present, I have to emit the "GoingToAddressIntent" regardless of what other slots are available. Given this requirement, I thought using a switch statement maybe more straightforward and readable despite having to have a few extra lines of code to "convert" the array of strings to an array of booleans. It clearly lays out the hierarchy & make sure they are evaluated in order. I could do:
if (_type === "IntentRequest") {
this.handler.state = states.START;
slots = [
_slots.indexOf("address") != -1,
_slots.indexOf("place") != -1,
_slots.indexOf("type") != -1,
_slots.indexOf("purpose") != -1
]
switch(slots.indexOf(true)) {
case 0:
this.emitWithState("GoingToAddressIntent");
break;
case 1:
this.emitWithState("GoingToAddressIntent");
break;
case 2:
this.emitWithState("GoingToTypeIntent");
break;
case 3:
this.emitWithState("GoingToPurposeIntent");
break;
default:
this.emit("Unhandled");
}
}
... in which case I have an extra line to define the array of booleans, use indexOf() to get the index of the first occurrence of a true literal (because all 4 slots are always in the order of hierarchy), and run it through the switch statement. However I wanted ask experts on their ideas of what best programming practice is in this case and the reasoning behind it because I want this to become a long-term project that is maintainable, and also I believe I can learn something from their insights.
Please leave a comment if you think this should be migrated to another community on SE, but from my research (although 3 years old) I believe this should be fine (I'm just not 100% confident on this).
If they're always in the order of precedence in _slots, maybe you could make a hash map to the state you're going to emit...
const map = {
address: "GoingToAddressIntent",
place: "GoingToPlaceIntent",
type: "GoingToTypeIntent",
purpose: "GoingToPurposeIntent"
};
const state = map[_slots[0]];
if (state) {
this.emitWithState(state);
} else {
this.emit("Unhandled");
}
I wouldn't go with your example of the switch statement. People could understand what you're attempting to do, but it does seem pretty convoluted. I use switch statements pretty liberally, mostly in backend code, and I think it could work fine here. A group of if/else is fine too, since there's only 4 cases you need to work through. Lets roll with the switch statement since that's what you're asking about.
Based on your explanation, the order is always going to be the same, although the first value you get may be different. So the solution would be to simply grab the first value, and switch over that.
if (!!slots.length) {
var keyword = slots[0];
switch (keyword) {
case 'address':
this.emitWithState("GoingToAddressIntent");
break;
case 'place':
this.emitWithState("GoingToPlaceIntent");
break;
case 'type':
this.emitWithState("GoingToTypeIntent");
break;
case 'purpose':
this.emitWithState("GoingToPurposeIntent");
break;
default:
this.emit('Unhandled'); // I typically throw an exception here
}
}

AngularJS soltuion to determining if any element in array1 exists in array2

I am working on an angularJS widget (my first) and I currently am looking for an angularJS solution to my problem.
Basically I have one array containing a list of string values.
var array1 = [
"Need to Know",
"Test Category 2",
"News"
];
and another array containing another list of string values
var array2 = [
"need to know",
"release notes",
"NEWS"
];
I need a true statement if any element from one array matches any element from the other array. The result also needs to be case insensitive.
Here is my current solution and works great.
angular.module("myWidget", function(...){
// angular code here
})
.service('arrayService', function() {
function arrayToLowerCase(array) {
return array.join("~!~").toLowerCase().split("~!~");
}
function arrayElementIsInArray(array1, array2) {
for (var i in array1) {
if (array2.indexOf(array1[i]) >= 0) {
return true;
}
}
return false;
}
function arrayCompare(array1, array2) {
return arrayElementIsInArray(arrayToLowerCase(array1), arrayToLowerCase(array2));
}
return {
arrayToLowerCase: arrayToLowerCase,
arrayElementIsInArray: arrayElementIsInArray,
arrayCompare: arrayCompare
};
})
the problem is my javascript coders (I primary work in c#) feel there is a more angularJS way to do this but they have brought nothing to the table as a definitive solution. It was suggested that the $filter module might be useful but I didn't see how it would exactly solve my problem.
If I already have the best solution, then awesome. If not please let me know what you think and lets go from there.
Thanks in advance.
EDIT: In response to some of the answers, I felt that I might have misinterpreted my request. What I am asking is there a built in function that angular provides that does this out of the box?
After researching this a bit more; the $filter Module will probably do it with a custom comparater implemented but that seems like way overkill for what I am looking for.
The current responses are all good stuff though. Thanks again!
Absolutely nothing to do with Angular. This is plain data structures and data manipulation. To say there should be a more AngularJS way of doing it would be like saying there should be a more MVC way to add two numbers.
Angular provides no basic data structures and utility set of functions beyond what is available in your browser's native list of array functions, which is different depending on which ECMAScript standard the browser supports.
You may want to look into a library like Lo-Dash for stuff like this (which you can use right along with Angular with no problems) as it's preferable to have proven code for these kind of data manipulations than to constantly have to debug your own.
With Lo-Dash, and remembering the requirement for case-insensitivity:
var array1Lowered = _.map(array1, function (value) { return value.toLowerCase(); });
var anyMatchesBool = _.any(array2, function (value) {
return _.contains(array1Lowered, value);
});
Note that I'm making the assumption that there will be no non-string items in either array.
Lo-Dash normalizes the API so you don't need to worry about what functions each browswer supports. If there's a native function, Lo-Dash will use it because it's faster. If not, Lo-Dash provides an all-JavaScript implementation.
Try this on for size. To me this really has nothing to do with Angular
(function(array1, array2) {
var tlc = function(a) { return a.toLowerCase(); };
array2 = array2.map(tlc);
array1 = array1.map(tlc);
return array1.filter(function(n) {
return array2.indexOf(n) != -1;
}).length > 0;
})(array1, array2);
Using native functions...
var intersection = array1.filter(function(n) {
return array2.indexOf(n) != -1
});
With help from Simplest code for array intersection in javascript

Creating and implementing an array from space delimited text: AngularJS

and thank you for your patience. I am an experienced coder using old school methods, and have used the old style of Javascript for many years. I've known about JQuery, and such as that for a while, but have been "too busy" to learn the new things. Well, I am now working on learning those things, and have chosen AngularJS.
I am watching the tutorial videos, and reading the documents and API reference for AngularJS, but the different nomenclature does impede my progress. But I'm trying. For me, using an example of a "real" sort of problem helps me to understand how to leverage the functionality.
In this example, I have an array of Power Ball numbers, and want to be able to paste in the space delimited winning numbers you might copy off of the Power Ball site. This would then parse the input and hi-lite all of the individual numbers. Eventually, perhaps hi-liting any winning combinations in different ways. No, this isn't a product I'm building, but I thought this would cause me to use many different tools in this tool box.
So my questions are:
I initialized the array in my service (factory) and have shown that I can push to it from my controller. Is this correct?
Is my current way of doing things (e.g. ng-class usage, etc.) at least feasible, if not correct?
How could I use space delimited input in the input/text box to do the comparison for the style changes?
Here is an example of my JavaScript code. (note: I'm only pasting in one set of numbers for the array.)
var lotteryCheckApp = angular.module('lotteryCheckApp', []);
lotteryCheckApp.factory('PowerBall', function() {
var PowerBall = {};
PowerBall.numArray = ["42"];
PowerBall.myNumbers = [
{
First_Number: '03',
Second_Number: '07',
Third_Number: '17',
Fourth_Number: '21',
Fifth_Number: '42',
PowerBall: '21'
}
];
return PowerBall;
});
lotteryCheckApp.controller('PowerBallNumbersCtrl',function($scope,PowerBall) {
$scope.powerball = PowerBall;
$scope.winningStyle = true;
$scope.powerball.numArray.push("21");
$scope.myCheck = function(searchNum,numVal) {
// the following code works for a simple input to value check
if(searchNum == numVal) return true;
else return false;
}
I have a JSFiddle link: http://jsfiddle.net/Divermarv/VpyxZ/1/
Again, thank you all for your patience and responses.
Glad to hear you're getting into angular. Here is how I would solve your problem JSFiddle
I use ng-change to watch the input values,
<input type="text" ng-model="input" ng-change="convert()">
and feed that to a method that converts the input to an array of values.
$scope.convert = function() {
$scope.hiLite = $scope.input.split(" ");
}
Then your check function checks to see if the value is in the array
$scope.myCheck = function(searchNum,numVal) {
if(searchNum.indexOf(numVal) !== -1) return true;
else return false;
}
Is that kind of what you were thinking?

Writing Javascript Algorithm, but I need to convert symbols into actual words

So, I'm writing a new JavaScript algorithm, codenamed jBam (Javascript Browser Algorithm Module), and it is used to write an extremely simple way to detect a browser and it's version. My main goal, is to be able to write this (with the library plugged in):
if(IE & version lessThan 9) {
// IE code for 8 and below
}
Right now, progress is going pretty good. I can write
if(browser == IE & version < 9)
So, back to my original question, the only thing I want to know is maybe someway set a variable for lessThan and set it equal to the symbol <. I've tried probably the most common remedy (var lessThan = <) but, of course, that's a little too wishful, and a little too easy. And, as we all know, nothing is ever too easy. Any suggestions, maybe a JavaScript preserve I'm not thinking of?
I agree with #JCOC611, but it's also possible that you make everything part of an object, and then make version an object as a property as well, where you can add a method called lessThan, so you would have something like:
var browserStuff = {
browser: {
value: "",
is: function (comparator) {
if (browserStuff.browser.value == comparator) {
return true;
}
return false;
}
},
version: {
value: "",
lessThan: function (comparator) {
if (browserStuff.version.value < comparator) {
return true;
}
return false;
}
},
init: function () {
// Do your stuff to determine the browser and its version
// You need to set browserStuff.version.value and browserStuff.browser.value
delete browserStuff.init;
}
}.init();
And then use it like:
if (browserStuff.browser.is("IE")) {
}
or
if (browserStuff.version.lessThan("7")) {
}
You'd have to add more things for "greater than" and "not equals" and whatnot, but that's just to start it off.
I don't exactly suggest this, because you can just as easily use normal Javascript operators to accomplish the same thing with less redundant code. This solution is more for a "readable" theme, but is in no way necessary or better.

Categories