How to refactor this condition without using if else - javascript

Hi I have following code :
const handleChange = ({ selectedItem }) => {
if (selectedItem?.value) {
if (name === FieldNames.SecurityQuestion1) {
resetField(FieldNames.FirstAnswer, '');
resetField(FieldNames.ConfirmFirstAnswer, '');
} else if (name === FieldNames.SecurityQuestion2) {
resetField(FieldNames.SecondAnswer, '');
resetField(FieldNames.ConfirmSecondAnswer, '');
}
}
};
Here, I am trying to refactor this without using the if else and switch condition. So I tried with the ternary operator but it also does not work:
selectedItem?.value ? (name === FieldNames.SecurityQuestion1) && resetField(FieldNames.FirstAnswer, '');
But this does not work as it is a wrong syntax. Can any one help me with this ?
Thanks.for the help.

I don't recommend refactoring, because it's perfectly readable code and it seems to work fine, but I think this would solve your problem:
selectedItem?.value ? ((name === FieldNames.SecurityQuestion1) && resetField(FieldNames.FirstAnswer, '')):null

If you just want to avoid nested ifs, why not put it in this way?
const handleChange = ({ selectedItem }) => {
if (!selectedItem?.value) {
return;
}
if (name === FieldNames.SecurityQuestion1) {
resetField(FieldNames.FirstAnswer, '');
resetField(FieldNames.ConfirmFirstAnswer, '');
} else if (name === FieldNames.SecurityQuestion2) {
resetField(FieldNames.SecondAnswer, '');
resetField(FieldNames.ConfirmSecondAnswer, '');
}
};
or this:
const handleChange = ({ selectedItem }) => {
if (selectedItem?.value && name === FieldNames.SecurityQuestion1) {
resetField(FieldNames.FirstAnswer, '');
resetField(FieldNames.ConfirmFirstAnswer, '');
} else if (selectedItem?.value && name === FieldNames.SecurityQuestion2) {
resetField(FieldNames.SecondAnswer, '');
resetField(FieldNames.ConfirmSecondAnswer, '');
}
};

I'm not sure I can recommend refactoring your code that way but... since you ask, here is a way to do it without if/else/switch.
const condition0 = selectedItem?.value;
const condition1 = name === FieldNames.SecurityQuestion1;
const condition2 = name === FieldNames.SecurityQuestion2;
const doThen = () => {
resetField(FieldNames.FirstAnswer, '');
resetField(FieldNames.ConfirmFirstAnswer, '');
};
const doElse = () => {
resetField(FieldNames.SecondAnswer, '');
resetField(FieldNames.ConfirmSecondAnswer, '');
};
condition0 && (
!(condition1 && (doThen() || true))
&& condition2 && doElse()
);

In my opinion you are refactoring the wrong thing. Obviously you are looking at your code and see that it is a bit of a mess with two places where the code seem to be repeated but not quite:
if (name === FieldNames.SecurityQuestion1) {
resetField(FieldNames.FirstAnswer, '');
resetField(FieldNames.ConfirmFirstAnswer, '');
} else if (name === FieldNames.SecurityQuestion2) {
resetField(FieldNames.SecondAnswer, '');
resetField(FieldNames.ConfirmSecondAnswer, '');
}
Realise that the ternary operator is just an if in expression context instead of a statement. Converting it will result in exactly the same mess with less readable syntax.
If you REALLY want to know the correct ternary expression for the code you have it looks exactly the same:
selectedItem?.value ?
name === FieldNames.SecurityQuestion1 ?
resetField(FieldNames.FirstAnswer, '')
:
resetField(FieldNames.ConfirmFirstAnswer, '')
:
name === FieldNames.SecurityQuestion2 ?
resetField(FieldNames.SecondAnswer, '')
:
resetField(FieldNames.ConfirmSecondAnswer, '');
The above is the correct way to abuse the ternary operator and get the same result. However, notice that it is still the same code and the same mess. And if you put the : in the wrong place it would be a bug and produce the wrong result!! The braces {} used by the if statement makes understanding and debugging the code much easier because it clarifies when a condition starts and when it ends.
The way to refactor this is to realize that Question1 and Question2 have common data. So structure it that way:
const FieldNames = {
Security: {
First: {
Question: '...',
Answer: '...',
Confirm: '...'
},
Second: {
Question: '...',
Answer: '...',
Confirm: '...'
}
}
}
Now you can refactor it like this:
const handleChange = ({ selectedItem }) => {
if (selectedItem?.value) {
for (let f of ['First', 'Second']) {
if (name === FieldNames.Security[f].Question) {
resetField(FieldNames.Security[f].Answer, '');
resetField(FieldNames.Security[f].Confirm, '');
}
}
}
};
This reduces duplicated code and makes your code DRY (and hence more compact) without reducing readability.

You can try this:
const handleChange = ({ selectedItem }) => {
const answerIndex = selectedItem?.value ? (name === FieldNames.SecurityQuestion1 ? 'FirstAnswer' : name === FieldNames.SecurityQuestion2 ? 'SecondAnswer' : null) : null;
if (answerIndex) {
resetField(FieldNames[answerIndex], '');
resetField(FieldNames[`Confirm${answerIndex}`], '');
}
}

const handleChange = ({ selectedItem }) => {
if (selectedItem?.value && name === FieldNames.SecurityQuestion1) {
resetField(FieldNames.FirstAnswer, '');
resetField(FieldNames.ConfirmFirstAnswer, '');
}
if (selectedItem?.value && FieldNames.SecurityQuestion2) {
resetField(FieldNames.SecondAnswer, '');
resetField(FieldNames.ConfirmSecondAnswer, '');
}
};
This is how I imagine a YouTuber I've seen who didn't like else statements would do it and addresses you concerns about the nested if/else statement.
<opinion> I rather think it's an improvement in terms of clarity and maintainability but as to whether it's the best way to set up the code - no comment. </opinion>

Related

Converting chain conditional operators to old check method

I'm having to convert my inline conditional operators in my node js application as they're not supported by PM2. I'm a little stuck on why one of my lines isn't converting correctly.
The current conditional chaining operators returns the correct result
// Working solution
purchases.forEach((purchase) => {
const matchingItems = sales.filter(obj => obj.elements[0]?.specialId === purchase.elements[0]?.specialId);
if (matchingItems.length > 0){
console.log('purchase has been SOLD.')
purchase.sold = true
}
})
but my converted code is not returning 'purchase has been sold'
purchases.forEach((purchase) => {
// const matchingItems = sales.filter(obj => obj.elements[0] && obj.elements[0].specialId === purchase.elements[0] && purchase.elements[0].specialId);
const matchingItems = sales.filter((obj) => {
return obj.elements[0] && obj.elements[0].specialId === purchase.elements[0] && purchase.elements[0].specialId
})
if (matchingItems.length > 0){
console.log('purchase has been SOLD.')
purchase.sold = true
}
})
https://jsfiddle.net/nia232/yugcw326/6/
I'm sure it's something obvious im doing wrong but any help appreciated!

Writing if/else statements with 3 conditions with a promise mixed in

So I have this conditional statement with 2 conditions, whereby
let modItemList = this.props.items
if (this.state.searchItemName) { // condition1
modItemList = (
this.props.items.filter(
(item) => item.name.toLowerCase().indexOf(lcName) !== -1 // For name
)
);
} else if (this.state.searchItemAddress) { //condition2
modItemList = (
this.props.items.filter(
(item) => item.fullAddress.some(e => e.toLowerCase().indexOf(lcAddress) !== -1) // For Address
)
);
}
This is where it's a little tricky to explain.
Now I want to add a 3rd condition, which happens only if both condition1 and condition2 are met, AND the outcome is that of executing code from condition1 and condition2.
How would I go about expressing that?
I think you just want to use two separate if conditions where both may run, not if/else if:
let modItemList = this.props.items;
if (this.state.searchItemName) { // condition1
modItemList = modItemList.filter(item =>
item.name.toLowerCase().indexOf(lcName) !== -1 // For name
);
}
if (this.state.searchItemAddress) { //condition2
modItemList = modItemList.filter(item =>
item.fullAddress.some(e => e.toLowerCase().indexOf(lcAddress) !== -1) // For Address
);
}
Nothing is asynchronous here or involves promises. If it did, I would recommend to just place an await in the respective location.
There's no asynchronous action here, so no need to track an async action with a promise.
Probably the simplest thing is to filter the filtered list:
let modItemList = this.props.items;
if (this.state.searchItemName) {
modItemList = modItemList.filter(item => item.name.toLowerCase().includes(lcName));
}
if (this.state.searchItemAddress) {
modItemList = modItemList.filter(item => item.fullAddress.some(e => e.toLowerCase().includes(lcAddress)));
}
Or filter once and check for searchItemName and searchItemAddress within the callback:
let modItemList = this.props.items.filter(item =>
(!this.state.searchItemName || item.name.toLowerCase().includes(lcName)) &&
(!this.state.searchItemAddress || item.fullAddress.some(e => e.toLowerCase().includes(lcAddress));
Even if the list is in the hundreds of thousands of entries, neither of those is going to be slow enough to worry about.
Or if it really bothers you do do that double-filtering or re-checking, build a filter function:
let modItemList;
let filterFunc = null;
if (this.state.searchItemName && this.state.searchItemAddress) {
filterFunc = item => item.name.toLowerCase().includes(lcName) && item.fullAddress.some(e => e.toLowerCase().includes(lcAddress));
} else if (this.state.searchItemName) {
filterFunc = item => item.name.toLowerCase().includes(lcName);
} else if (this.state.searchItemAddress) {
filterFunc = item => item.fullAddress.some(e => e.toLowerCase().includes(lcAddress));
}
modItemList = filterFunc ? this.props.items.filter(filterFunc) : this.props.items;
That involves repeating yourself a bit, though, leaving open the possibility that you'll update one address filter but not the other. You can aggregate the filter functions:
let nameCheck = item => item.name.toLowerCase().includes(lcName);
let addressCheck = item => item.fullAddress.some(e => e.toLowerCase().includes(lcAddress));
let modItemList;
if (this.state.searchItemName && this.state.searchItemAddress) {
modItemList = this.props.items.filter(item => nameCheck(item) && addressCheck(item));
} else if (this.state.searchItemName) {
modItemList = this.props.items.filter(nameCheck);
} else if (this.state.searchItemAddress) {
modItemList = this.props.items.filter(addressCheck(item);
}
If there were more than two, we might look at putting them in an array and doing
modItemList = this.props.items.filter(item => arrayOfFunctions.every(f => f(item)));
So...lots of options. :-)
I've used includes(x) rather than indexOf(x) !== -1 above. I find it clearer.
You would still need to wait with the action till promise is resolved and finished. So you would check the conditions inside of promise callback and then make adequate actions. Until you have resolved promise, you can display some "loading" information.
Maybe this solution You want?
if (condition1 & condition2) {
something = this.props.something.filter(1)).then(this.props.something.filter(2)
} else if (condition1) {
something = this.props.something.filter(1)
} else if (condition2) {
something = this.props.something.filter(2)
}

simplify an if/else inside another else

I have a piece of code, i need to put an if/else inside an else to change the DOM of my view:
else {
this.selectedUser = this.user.find(b => b._id === userId)
this.onSelect.emit(this.selectedUser)
if (this.selectedUser.isConnected) {
(<HTMLImageElement>document.getElementById('user-state')).src = 'assets/imgs/userConnected.png'
} else {
(<HTMLImageElement>document.getElementById('user-state')).src
= 'assets/imgs/userDiscoonnected.png'
}
}
Is there a way to simplify this ? or make it more clean ?
only readable way I can think of is
else {
this.selectedUser = this.user.find(b => b._id === userId)
this.onSelect.emit(this.selectedUser)
(<HTMLImageElement>document.getElementById('user-state')).src =
this.selectedUser.isConnected
? 'assets/imgs/userConnected.png'
: 'assets/imgs/userDiscoonnected.png';
}
else {
this.selectedUser = this.user.find(b => b._id === userId)
this.onSelect.emit(this.selectedUser)
var imgPath = this.selectedUser.isConnected ? 'assets/imgs/userConnected.png' : 'assets/imgs/userDiscoonnected.png';
(<HTMLImageElement>document.getElementById('user-state')).src = imgPath;
}
A ternary operator looks nice.
You can use ? : to make a simpler if statement when you are comparing with a bool.
The statement on the left side of the ? is the condition to be fulfilled. To the left of the : you place the value of the variable if the condition is true. On the right side of the : you place what the value should be if the condition turns out to be false.
var src = this.selectedUser.isConnected ? 'assets/imgs/userConnected.png' : 'assets/imgs/userDiscoonnected.png';
<HTMLImageElement>document.getElementById('user-state')).src = src;

JavaScript ternary operator into full if/else statement issue

I have following ternary statement:
$.history.init(function(url) {
load(url == "" ? "#some-page" : url);
});
Which I have rewrote into:
$.history.init(function(url) {
load(
if( url == ""){ url = "#some-page"
} else { url = url }
);
});
I now the is an error on line 3 if(url == ""), but I don't understand what error.
Any suggestion much appreciated.
In JavaScript, an if is not an expression. It does not return a value and cannot be put inside a function call. That is, this is not valid:
func(if (a) { ... } else { ... });
This is the main difference between if and ?:--the operator is an expression and returns a value; if is a statement, does not return a value and cannot be used everywhere.
Your best bet if you have to avoid the ternary operator is to do something like:
if (url == "") {
url = "#some-page";
}
load(url);
You can also achieve the same effect using ||:
function (url) {
load(url || "#some-page");
}
This is the shortest and most idiomatic way to write your code.
if expressions dont return anything in JS. So that basically does load(undefined).
Try this instead:
if (url === '') {
url = '#some-page';
}
load(url);
Note you don't need to else at all, because if the value is present you have nothing to change.
rewrite it as
$.history.init(function(url) {
if( url == ""){
url = "#some-page";
}
load( url );
});
Your rewritten code is invalid. Try this:
$.history.init(function(url) {
if(url == "") {
load("#some-page");
} else {
load(url);
}
});
You need the if statement to be outside of the load function, i.e.
$.history.init(function(url) {
if (url === "") {
url = "#some-page";
}
load(url);
});
Note that you don't need the else clause as url = url is a redundant operation.

Some problem with jquery form

This my code:
$(document).ready(function() {
$('.submit').click(function() {
var answer_text = $("#answer_text").val();
if (answer_text === '' || undefined === $("input[name='answer[scale]']:checked").val()) {
alert('error!!');
return false;
}
else {
alert('yeah! cool baby!');
}
}
});
Problem: jQuery doesn't see the ||. I don't know what to do. I tried to do something like:
if
else if
else
or
if
else
if
else
Don't know what to do. please help me, maybe some error and mistakes with OR operator? or what?
To know if no checkbox was checked just use the length, no need to mess with the value:
if (answer_text === '' || $("input[name='answer[scale]']:checked").length === 0) {
//answer text is empty and no answer scale checkbox was checked
}
i guess what you wanted to do is
if (answer_text === '' || $("input[name='answer[scale]']:checked").val()==="undefined"){
you have got the operands on the wrong side of the operator
Try to wrap it in proper braces and check.
if ((answer_text === '') || (undefined === $("input[name='answer[scale]']:checked").val()))
$(document).ready(function() {
$('.submit').click(function() {
var answer_text = $("#answer_text").val();
if (answer_text == ''){
if ( undefined === $("input[name='answer[scale]']:checked").val()){
alert('error!!');
return false;
}
else{
alert('yeah! cool baby!');
}
}
else{
alert('yeah! cool baby!');
}
}
}
This is not the fastest way but it will do it...

Categories

Resources