Given the following code:
if (CONDITION1) {
if (CONDITION2) {
if (CONDITION3) {
if (CONDITION4) {
if (CONDITION5) {
} else {
return ('STOP IN CONDITION5')
}
} else {
return ('STOP IN CONDITION4')
}
} else {
return ('STOP IN CONDITION3')
}
} else {
return ('STOP IN CONDITION2')
}
} else {
return ('STOP IN CONDITION1')
}
I need to replace these multiple if statements for some other code using good practices and for cleaner reading for other people in future.
You could flatten this by testing for not condition:
function doStuff() {
if (!CONDITION1) {
return ('STOP IN CONDITION1')
}
if (!CONDITION2) {
return ('STOP IN CONDITION2')
}
if (!CONDITION3) {
return ('STOP IN CONDITION3')
}
if (!CONDITION4) {
return ('STOP IN CONDITION4')
}
if (!CONDITION5) {
return ('STOP IN CONDITION5')
}
}
Note: in between the ifs you could add code for condition true.
This has already been answered and I wouldn't recommend this version (linters probably complain, flat if statements are fine) but I'm throwing it in there to show another possibility.
You can potentially use a switch statement and add your conditions to each case:
const CONDITION1 = 1 > 2
const CONDITION2 = 2 < 1
const run = () => {
switch(true) { // always evaluate this switch
case !CONDITION1:
return 'STOP IN CONDITION1'
case !CONDITION2:
return 'STOP IN CONDITION1'
// etc...
}
}
console.log(run())
Using Array.prototype.findIndex()
Store your conditions into Array
Use Array.prototype.findIndex(). Just return the argument itself (x) => x, it fill be treated as boolean and if true the method will return the index. Store the returned integer into a variable errIndex.
If errIndex is greater than -1 that means you have an "error" in the conditions array at that index
const conditions = [false, false, true, false, false];
const errIndex = conditions.findIndex(x => x);
if (errIndex > -1) {
console.log(`STOP IN CONDITION ${errIndex + 1}`);
} else {
console.log("All passed");
}
The nice thing about using .findIndex() is in that it can short-circuit the search and immediately return the index.
Not sure why, but given you want to return a String, and since return statements are used within a function, and since it's always preferable for a function to return the same type,
you could create a function that accepts an array of conditions and a success string as the second argument:
const status = (conditions, successMsg) => {
const idx = conditions.findIndex(x=>x);
return idx < 0 ? successMsg : `STOP IN CONDITION ${idx+1}`;
};
console.log(status([false, false, true, false, false], "OK")); // STOP IN CONDITION 3
console.log(status([false, false, false, false, false], "OK")); // OK
or you can create something similar based on the above idea, like a pass function in which you do not hardcode error strings, but instead it provides you with a callback function with argument being an Object with index property or null
const pass = (conditions, fn) => {
const index = conditions.findIndex(x => x);
fn(index < 0 ? null : {index});
};
pass([false, false, true, false, false], (err) => {
if (err) {
console.log(`STOP IN CONDITION ${err.index + 1}`)
return; // Exit function here
}
// All OK...
console.log("All OK! Do some work here");
});
Related
I need to return true or false based on some condition, I am not getting the cleaner code to do this:
I need to hide or show some elements on UI based on this condition which should return true or false:
scope.isDataEnabled =function(options){ //Here if supppose a comes,
// then i need to send true, otherwise false
if(!$rootScope.currentProduct.id ==
ProductConstants.BB_PRODUCT_ID){
return true;
}
else{
_.map( $rootScope.currentUser.permissions, permissionObj =>{
// permissions is an Array
return (permissionObj.module == "DATA" &&
permissionObj.values == "b" && options=="a") //need to send true
//if this condition satisfies
});
}
return false; //after returning true,
//it comes to this line and sets to false
}
Please help me guide, what i should use, to achieve it. I was thinking of using ternary operator, but not getting how to use it inside map.
Simplifying your code to a minimal test scenario that can be manipulated (and just use plain old ES20xx) may help. Something like:
let $rootScope = {
currentProduct: {
id: `x`
},
currentUser: {
permissions: [{
module: `BB_DATASET`,
values: `b`
}, {
module: `some`,
values: `a`
}]
}
};
const ProductConstants = {
BB_PRODUCT_ID: `y`
}
console.log(testMe(`a`)); // true (currentProduct.id not BB_PRODUCT_ID)
ProductConstants.BB_PRODUCT_ID = `x`;
console.log(testMe(`a`)); // true (module && values && options)
$rootScope.currentUser.permissions[0].module = `AA_DATASET`;
console.log(testMe(`a`)); // false (module not BB_DATASET)
$rootScope.currentUser.permissions[0].module = `BB_DATASET`;
console.log(testMe(`b`)); // false (options not `a`)
$rootScope.currentUser.permissions[0].values = `c`;
console.log(testMe(`a`)); // false (values not `b`)
function testMe(options) {
if ($rootScope.currentProduct.id !== ProductConstants.BB_PRODUCT_ID) {
return true;
}
const found = $rootScope.currentUser.permissions.find( v =>
v.module === `BB_DATASET` &&
v.values === `b` &&
options == `a`);
return found ? true : false;
}
.as-console-wrapper {
top: 0;
max-height: 100% !important;
}
Firstly, the return inside of the map function will not return the value to the outer function, you are only returning the result to the map function itself. The result of the map function is never being returned.
Also, I think you might want to use _.some instead of map. map is used for transforming data, while some is used to check if something matches a condition.
return _.some($rootScope.currentUser.permissions, permissionObj => {
return (permissionObj.module == "BB_DATASET"
&& permissionObj.values == "b"
&& options=="a")
});
This will check if any of the permissions match the given condition. Alternatively you can use _.all if you want everything to match the condition.
I have a class for validation which have different validation functions.
Now what i am trying to do is make an object in another file and send all the validation data using a constructor.This constructor will receive an object that looks like this "rules[is_empty:value]". In this left part is function name and value is the value fetched from input field.Now how do I call the function and send the value as an argument.Also what should i do when there are functions that has more than one argument.
I have already tried using map method and split method but not able to access the function.
class Validator {
constructor(rules) {
let rule_obj = {}
// rules[is_empty:value,has_valid_min_length:2;3]
}
/**this is to check if the field has an empty value or not */
is_empty = (value) => {
if (value == '' || value == null) {
return true
}
else {
return false
}
}
/**this is to check if value has required minimum length or not */
has_valid_min_length = (value, min_length = 0) => {
if (this.is_empty(value)) {
return false
}
if (value.length < min_length) {
return false
}
else {
return true
}
}
/**this is to check if value has more characters than maximum length */
has_valid_max_length = (value, max_length = 0) => {
if (this.is_empty(value)) {
return false
}
if (value.length > max_length) {
return false
}
else {
return true
}
}
//this is to check if selected date is less than given limit
is_before_min_date = (value_date, min_date) => {
if (this.is_empty(value)) {
return false
}
if (value_date < min_date) {
return true
}
else { return false }
}
//this is to check if selected date is higher than given limit
is_after_max_date = (value_date, max_date) => {
if (this.is_empty(value)) {
return false
}
if (value_date > max_date) {
return true
}
else {
return false
}
}
}
I want to call the function which is before ':' sign in the array and give that function argument which is in value that is at the right side of ':'.
Please help.
You could send an object through the constructor...
x = {
"is_empty": [0, 2, 2],
"has_valid_min_length": [ [value, min_length], [value, min_length] ],
"has_valid_max_length": [ [value, max_length], [value, max_length] ],
"is_before_min_date": [ [value_date, min_date], [value_date, min_date] ],
"is_after_max_date": [ [value_date, max_date], [value_date, max_date] ]
}
and then in your constructor, set up a loop through the object and value arrays...
constructor(to_be_validated) {
let validator_names = Object.keys(to_be_validated);
for (let validator of validator_names) {
let values = to_be_validated[validator];
if (validator === "is_empty") {
for (let value of values) {
this.is_empty(value);
}
} else if (validator === "has_valid_min_length") {
for (let value of values) {
this.has_valid_min_length(value[0], value[1]);
}
} etc...
}
}
and then when you call the function, the methods should execute
let my_validator = new Validator(x);
I echo the comment(s) above. Wanting the approach and specific syntax of,
let my_validator = new Validator(rules[validator_name:values]);
all in one clean line like that, is a bit off. I've never seen it done like that.
You probably want an additional function in your class that calls all the check-functions according to your rules.
class Validator {
constructor(rules) {
this.rules = rules;
// rules={is_empty:[],has_valid_min_length:[2]};
}
validate = (value) => {
let pass = true;
Object.keys(this.rules).forEach(k=>pass = pass && this[k](value, ...this.rules[k]));
return pass;
}
...
}
The rules-Objects has key-value-pairs, where the keys are the function-names of the individual checks and the values are arrays of parameters that will be passed. This array can have zero or more entries.
Edit: forEach will iterate over all the keys. On every turn k will hold the current key. pass is a boolean that collects all the return values of your checker-functions. (true && false === false) That way the final return-value will be false if any of the checks returned false.
You would then apply the rules to your data like this:
myValidatorObj.validate(data);
I have a problem to solve using some data from an object. The data could take a few forms and may or may not exist in the first place. For example
things : {
oranges: true,
apples : false
}
but it could equally be:
things : {
oranges: false,
apples : false
}
or maybe things doesn't even exist
I need to:
1) Determine that things exists
2) Determine that things contains further keys
These two statements need to be verified in one callable function e.g thingsHasData()
3) If things does have data, is any of the data set to true?
This also needs to be a callable function e.g fruitsIsTrue()
4) Return the key for one of the true values
trueFruit() - this should only return one key, but it doesn't matter which (it shouldn't ever have two true values as per business rules but it's more of a fallback to just return one if for some reason it does)
So I've been able to get the key of a true key-value pair using the following:
var thingsList = {
things : {
oranges: false,
apples : true
}
}
var trueFruit = Object.keys(thingsList).filter(function(key) {
return thingsList[key];
});
return thingsList[0];
This correctly returns apples and only apples so it works for point 4 but not the others, and I feel like there is a better way to do this not having to rely on repeating the same .filter in a few different functions. Ideas?
You could take functions and for a true value, use Array#find.
function thingsHasData(object) {
return 'things' in object;
}
function fruitsIsTrue(object) {
return 'things' in object && Object.values(object.things).some(Boolean);
}
function trueFruit(object) {
return 'things' in object && Object.keys(object.things).find(k => object.things[k]);
}
var a = {},
b = { things: {} },
c = { things: { oranges: true, apples : false } },
d = { things: { oranges: false, apples : false } };
[a, b, c, d].forEach(o => console.log(
thingsHasData(o),
fruitsIsTrue(o),
trueFruit(o)
));
To check if the Object things exist, you can use the following code:
if (typeof things != "undefined") {
// It exists!
}
To check if an object has any children, check Object.keys(things).length > 0.
So the check for 1) and 2) would look like:
let things = {
oranges: true,
apples: false
}
if (typeof things != "undefined") {
// It exists!
if (Object.keys(things).length > 0) {
// It has children!
}
}
var thingsList = {
things : {
oranges: false,
apples : true
},
things2 : {
oranges: true,
apples : true
}
};
function validateThings(things) {
// (1) checks for a falsy value of things
if (!things) {
return false;
}
var keys = Object.keys(things);
// (2) checks if things has keys
if (!keys.length) {
return false;
}
// (3) then it checks for every single keys value if it is truthy
for (var i = 0, len = keys.length; i < len; i++ ) {
if (things[keys[i]]) {
// (4) return this value — all tests passed
return things[keys[i]];
}
}
return false;
}
console.log(validateThings(thingsList.notInList));
console.log(validateThings(thingsList.things));
console.log(validateThings(thingsList.things2));
const thingsHasData = arg => (arg.things && Object.keys(arg.things).length>0) ? true : false;
const trueFruit = arg => {
if (!arg.things) return;
let fruitIndex = null;
let fruitValues = Object.values(arg.things);
fruitValues.forEach((value, index) => {
if (value) fruitIndex = Object.keys(arg.things)[index];
});
return fruitIndex;
}
Below is the object in which I need to find at least one occurrence of isSelected: true.
[
{
"isSelected": true,
"child": [
{
"isSelected": true,
"child": [
{
"isSelected": true,
"child": [
{
"isSelected": true
}
]
}
]
}
]
}
]
The above object can have n elements in it and each element can have n children and so on. For every element there will be an isSelected key with value "true/false".
I am trying to write a function in JavaScript that will return true if it finds at least one occurrence of isSelected key with true value.
Wrote below function using JSON.stringify() and search for string "isSelected:true" string in it
function hasIsSelected(data){
return (JSON.stringify(data)).search('"isSelected":true') > -1 ? true: false
}
Not sure if JSON.stringify() will be efficient for large objects.
Trying to find solution in JavaScript without using third party library.
You can use a recursive algorithm to check the "isSelected" value and loop over all the children :
function hasIsSelected(data) {
if (data.isSelected) {
return true;
}
if (data.child) {
for (var i = 0; i < data.child.length; i++) {
if (hasIsSelected(data.child[i])) {
return true;
}
}
}
return false;
}
var json = [...]; // Your value
hasIsSelected(json[0]);
EDIT :
Ok let's make a very simple benchmark for the worst case :
function createTestData(depth) {
var obj = { isSelected: depth === 0 };
if (depth > 0) {
obj.child = [createTestData(depth - 1)];
}
return obj;
}
var testData = [createTestData(1000)]; // Big object, the "true" value is in the deepest child.
function hasIsSelectedStrinfigy(data){
return (JSON.stringify(data)).search('"isSelected":true') > -1;
}
function hasIsSelectedRec(data) {
if (data.isSelected) {
return true;
}
if (data.child) {
for (var i = 0; i < data.child.length; i++) {
if (hasIsSelectedRec(data.child[i])) {
return true;
}
}
}
return false;
}
// Using NicolaeS's solution
function hasIsSelectedRecTOC(data) {
if (data.isSelected === true) {
return true;
}
if (data.child instanceof Array) {
// stops after the first valid element
return data.child.some(hasIsSelectedRecTOC);
}
return false;
}
// Run tests
function runTest(fun) {
var t0 = performance.now();
fun(testData[0]);
var t1 = performance.now();
return t1 - t0;
}
console.log("Exec time using stringify : %o", runTest(hasIsSelectedStrinfigy));
console.log("Exec time using recursion : %o", runTest(hasIsSelectedRec));
console.log("Exec time using recursion with TOC : %o", runTest(hasIsSelectedRecTOC));
Results on my computer (change every time you run them but you get the idea) :
Exec time using stringify : 6.785000000000004
Exec time using recursion : 0.36999999999999034
Exec time using recursion with TOC : 0.37999999999999545
This was for the worst case. Now with the best case (the first isSelected is "true") :
function createTestData(depth) {
var obj = { isSelected: true }; // isSelected is always true
if (depth > 0) {
obj.child = [createTestData(depth - 1)];
}
return obj;
}
var testData = [createTestData(1000)];
Results :
Exec time using stringify : 3.980000000000002
Exec time using recursion : 0.040000000000000924
Exec time using recursion with TOC : 0.02499999999999858
Building on the answer of #Junior - recursion is the fastest way to do it, but here is a more performant version using tail call optimization:
function hasIsSelected(data) {
if (data.isSelected === true) {
return true;
} else if (data.child instanceof Array) {
return data.child.some(hasIsSelected); // stops after the first selected element
} else return false;
}
Another important trick is to stop the loop as soon as a true is found.
Recursion will be the best way to do that:
const deepSearch = (arr) => {
return arr.some((v) => {
if (v.isSelected === true) {
return true;
}
if (Array.isArray(v.child) && v.child.length > 0) {
return deepSearch(v.child);
}
return false;
});
};
Here is jsperf test.
Added: Array.isArray(X) is ≈3.3 times faster than X instanceof Array. Here is jsperf test confirming that.
Let's say we have this JavaScript object:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
How can we check if value property exists?
I can see only two ways:
First one:
if(object && object.innerObject && object.innerObject.deepObject && object.innerObject.deepObject.value) {
console.log('We found it!');
}
Second one:
if(object.hasOwnProperty('innerObject') && object.innerObject.hasOwnProperty('deepObject') && object.innerObject.deepObject.hasOwnProperty('value')) {
console.log('We found it too!');
}
But is there a way to do a deep check? Let's say, something like:
object['innerObject.deepObject.value']
or
object.hasOwnProperty('innerObject.deepObject.value')
There isn't a built-in way for this kind of check, but you can implement it easily. Create a function, pass a string representing the property path, split the path by ., and iterate over this path:
Object.prototype.hasOwnNestedProperty = function(propertyPath) {
if (!propertyPath)
return false;
var properties = propertyPath.split('.');
var obj = this;
for (var i = 0; i < properties.length; i++) {
var prop = properties[i];
if (!obj || !obj.hasOwnProperty(prop)) {
return false;
} else {
obj = obj[prop];
}
}
return true;
};
// Usage:
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
}
console.log(obj.hasOwnNestedProperty('innerObject.deepObject.value'));
You could make a recursive method to do this.
The method would iterate (recursively) on all 'object' properties of the object you pass in and return true as soon as it finds one that contains the property you pass in. If no object contains such property, it returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
function hasOwnDeepProperty(obj, prop) {
if (typeof obj === 'object' && obj !== null) { // only performs property checks on objects (taking care of the corner case for null as well)
if (obj.hasOwnProperty(prop)) { // if this object already contains the property, we are done
return true;
}
for (var p in obj) { // otherwise iterate on all the properties of this object.
if (obj.hasOwnProperty(p) && // and as soon as you find the property you are looking for, return true
hasOwnDeepProperty(obj[p], prop)) {
return true;
}
}
}
return false;
}
console.log(hasOwnDeepProperty(obj, 'value')); // true
console.log(hasOwnDeepProperty(obj, 'another')); // false
Alternative recursive function:
Loops over all object keys. For any key it checks if it is an object, and if so, calls itself recursively.
Otherwise, it returns an array with true, false, false for any key with the name propName.
The .reduce then rolls up the array through an or statement.
function deepCheck(obj,propName) {
if obj.hasOwnProperty(propName) { // Performance improvement (thanks to #nem's solution)
return true;
}
return Object.keys(obj) // Turns keys of object into array of strings
.map(prop => { // Loop over the array
if (typeof obj[prop] == 'object') { // If property is object,
return deepCheck(obj[prop],propName); // call recursively
} else {
return (prop == propName); // Return true or false
}
}) // The result is an array like [false, false, true, false]
.reduce(function(previousValue, currentValue, index, array) {
return previousValue || currentValue;
} // Do an 'or', or comparison of everything in the array.
// It returns true if at least one value is true.
)
}
deepCheck(object,'value'); // === true
PS: nem035's answer showed how it could be more performant: his solution breaks off at the first found 'value.'
My approach would be using try/catch blocks. Because I don't like to pass deep property paths in strings. I'm a lazy guy who likes autocompletion :)
JavaScript objects are evaluated on runtime. So if you return your object statement in a callback function, that statement is not going to be evaluated until callback function is invoked.
So this function just wraps the callback function inside a try catch statement. If it catches the exception returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
const validate = (cb) => {
try {
return cb();
} catch (e) {
return false;
}
}
if (validate(() => obj.innerObject.deepObject.value)) {
// Is going to work
}
if (validate(() => obj.x.y.z)) {
// Is not going to work
}
When it comes to performance, it's hard to say which approach is better.
On my tests if the object properties exist and the statement is successful I noticed using try/catch can be 2x 3x times faster than splitting string to keys and checking if keys exist in the object.
But if the property doesn't exist at some point, prototype approach returns the result almost 7x times faster.
See the test yourself: https://jsfiddle.net/yatki/382qoy13/2/
You can also check the library I wrote here: https://github.com/yatki/try-to-validate
I use try-catch:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
var object2 = {
a: 10
}
let exist = false, exist2 = false;
try {
exist = !!object.innerObject.deepObject.value
exist2 = !!object2.innerObject.deepObject.value
}
catch(e) {
}
console.log(exist);
console.log(exist2);
Try this nice and easy solution:
public hasOwnDeepProperty(obj, path)
{
for (var i = 0, path = path.split('.'), len = path.length; i < len; i++)
{
obj = obj[path[i]];
if (!obj) return false;
};
return true;
}
In case you are writing JavaScript for Node.js, then there is an assert module with a 'deepEqual' method:
const assert = require('assert');
assert.deepEqual(testedObject, {
innerObject:{
deepObject:{
value:'Here am I'
}
}
});
I have created a very simple function for this using the recursive and happy flow coding strategy. It is also nice to add it to the Object.prototype (with enumerate:false!!) in order to have it available for all objects.
function objectHasOwnNestedProperty(obj, keys)
{
if (!obj || typeof obj !== 'object')
{
return false;
}
if(typeof keys === 'string')
{
keys = keys.split('.');
}
if(!Array.isArray(keys))
{
return false;
}
if(keys.length == 0)
{
return Object.keys(obj).length > 0;
}
var first_key = keys.shift();
if(!obj.hasOwnProperty(first_key))
{
return false;
}
if(keys.length == 0)
{
return true;
}
return objectHasOwnNestedProperty(obj[first_key],keys);
}
Object.defineProperty(Object.prototype, 'hasOwnNestedProperty',
{
value: function () { return objectHasOwnNestedProperty(this, ...arguments); },
enumerable: false
});