simplify an if/else inside another else - javascript

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;

Related

How to refactor this condition without using if else

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>

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)
}

Dynamically handle string and array in code

Datatype of stack id either can be an array or a string.
In the below code stack[0].id is Array and stack[1].id is string.
Issue is stackConfig is undefined when id is returned as array.
How do i handle this dynamically?
let stack = [{id:['stack1','stack2']},{id:'stack2'}]
let stackConfig = this.stackConfigs.find(c => c.id === selectionId);
You could try something like this:
let stack = [{id:['stack1','stack3']},{id:'stack2'},{id:'stack4'}]
let selectionId = 'stack2';
let stackConfig = stack.find(c => {
if(Array.isArray(c.id)) { if (c.id.indexOf(selectionId) != -1) return true;}
else { return c.id === selectionId }
return false;
});
console.log(stackConfig);
The first thing you should do is check whether c.id === selectionId is true at any point. This might never be true, hence why it is undefined.
You could try to handle having the selectionId also undefined as follows:
if (c.id.indexOf(selectionId) != -1) return true;

Checking image name using javascript

I'm changing the img src on click using javascript.
I'm trying to determine whether to switch on or off.
I'm testing the following:
var img_el = document.getElementById("on_off_img");
if ( img_el.src == 'img/on.png' ) {
img_el.src = 'img/off.png'
} else {
img_el.src = 'img/on.png'
}
My problem is that i never get a match - it looks like img_el.src returns the full URL... Is there a function to just test the actual filename instead of the full string to the file?
Or is there a better way to manage the click?
use indexOf() instead of comparing the src
e.g
var img_el = document.getElementById("on_off_img");
if ( img_el.src.indexOf('on.png') > -1) {
img_el.src = 'img/off.png'
} else {
img_el.src = 'img/on.png'
}
Yuo can always use indexOf:
if(img_el.src.indexOf('img/on.png') > -1){
img_el.src = 'img/off.png'
}else{
img_el.src = 'img/on.png'
}
To shorten this even more, you can use a ternary operator:
var img_el = document.getElementById("on_off_img"),
isOn = img_el.src.indexOf('on.png')>-1;
img_el.src = isOn ? 'img/off.png' : 'img/on.png';
You can use match statement aswell.
var img_el = document.getElementById("on_off_img");
if ( img_el.src.match("on.png"))
{
img_el.src = 'img/off.png'
} else
{
img_el.src = 'img/on.png'
}
Try using JQuery:
$("#on_off_img").click(function(){
if($(this)[0].nameProp == "on.png")
$(this).attr("src","img/off.png");
else
$(this).attr("src","img/on.png");
});

Categories

Resources