Converting chain conditional operators to old check method - javascript

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!

Related

condense if, else JS with similar condition rules

trying to find a way to condense this. wasnt sure of the best way to do it. basically if criteria is met i display an alert with a parameter that is the message. i was thinking of maybe trying it in function. this is part of a larger function react component. i was also thinking if i could find a way to condense the else if's i could use a ternary. thanks in advance for the assistance.
const handleUpdatePassword = () => {
const allFilled = !reject(passwords).length;
const passwordsMatch = newPassword === conPassword;
const isDifferent = curPassword !== newPassword;
const meetsPasswordRequirements = validatePassword();
const usesName = isUsingName();
const usesUserID = isPartOfUserID();
const isValidPassword = meetsPasswordRequirements && isDifferent;
if (allFilled) {
if (!isDifferent) {
Alert.alert(difPassWord);
} else if (!passwordsMatch) {
Alert.alert(noMatch);
} else if (!meetsPasswordRequirements) {
Alert.alert(pasReqs);
} else if (usesName || usesUserID) {
Alert.alert(pasName);
}
} else {
Alert.alert(fieldNotComplete);
}
if (isValidPassword) {
changePasswordPost(
{
userId,
curPassword,
newPassword
},
partyId
);
}
};
You can create an array of objects for your validation rules, each containing a function which returns a boolean indicating whether that validation passes, and a string with the error message to display.
Then loop over the rules array and alert the message for the first rule that returns false. If they all return true, do the post.
You can split each if statement into a function, then chain them. For example
// here we make a closure to validate, and return a Promise
// condition can be a function
const validate = (condition, error) => ()=> new Promise((res, rej)=>{
if(condition()){
res();
}else{
rej(error);
}
});
const handleUpdatePassword = () => {
const validateFieldsComplete = validate(
()=>!reject(passwords).length,
fieldNotComplete
);
const validateDifPassword = validate(
()=> curPassword !== newPassword,
difPassWord
);
// ...
validateFieldsComplete()
.then(validateDifPassword)
.then(...)
.catch(Alert.alert)
}
It would be much cleaner with pipe. You can take a look at ramda. Or if you are intrested in functional way, you might consider using Monad.
I'd recommend DRYing up the Alert.alert part since all branches have that in common, and just come up with an expression that evaluates to the alert message. Compactness isn't always everything, but if you want it, then nested conditional operators can fit the bill. I'm also rearranging your conditions so that it can be a flat chain of if/elses:
const message
= reject(passwords).length ? fieldNotComplete
: curPassword === newPassword ? difPassWord
: newPassword !== conPassword ? noMatch
: !validatePassword() ? pasReqs
: (isUsingName() || isPartOfUserID()) ? pasName
: null;
const isValid = !message;
if (!isValid) {
Alert.alert(message);
}
(feel free to use any other sort of code formatting pattern; nested conditionals always look awkward no matter which pattern you use, IMO.)
Edit:
Also inlined conditionals which will short-circuit evaluation and make it even more compact.
I'd setup a validations object that has the tests and error messages and then loop over it. If validation fails, it'll throw the last validation error message. Using this method, you only have to maintain your tests in one place and not mess with a block of conditional statements.
const handleUpdatePassword = () => {
const validations = {
allFilled: {
test() {
return newPass && oldPass
},
error: 'Must fill out all fields'
},
correct: {
test() {
return curPass === oldPass
},
error: 'Incorrect password'
},
[...]
}
const invalid = () => {
let flag = false
for (let validation in validations) {
if (!validations[validation].test()) {
flag = validations[validation].error
}
}
return flag
}
if (invalid()) {
Alert.alert(invalid())
} else {
changePasswordPost(
{
userId,
curPass,
newPass
},
partyId
)
}
}
hi everyone this was the method i used for a solution
const messages = [
{
alertMessage: difPassWord,
displayRule: different()
},
{
alertMessage: noMatch,
displayRule: match()
},
{
alertMessage: pasReqs,
displayRule: validatePassword()
},
{
alertMessage: pasName,
displayRule: !isUsingName() || !isPartOfUserID()
}
];
if (allFilled) {
const arrayLength = messages.length;
for (let i = 0; i < arrayLength; i++) {
if (messages[i].displayRule === false) {
Alert.alert(messages[i].alertMessage);
}
}

How to optimize filter JS?

There is a following filter code:
const foundResult = this.visitors.filter((p: IVisitor) => {
let found = false;
if ('qrcode' in this.scanResponse) {
if (this.scanResponse && p.code && this.scanResponse.qrcode) {
found = p.code.toLowerCase() === this.scanResponse.qrcode.toLowerCase();
}
} else {
if (this.scanResponse && p.document_number && this.scanResponse.document_number) {
found = p.document_number.toString().toLowerCase() === this.scanResponse.document_number.toString().toLowerCase();
}
}
return found;
});
Problem is this.visitors contains over 1000 records, so I took a memory screen this operation takes 5 seconds for looking.
How to optimize this filter, any suggestions?
I know that if statement is not good in loop, but why it works so long time?
Well there isn't much to optimise in this example without knowing the rest of the app.
I would remove all repeating computations out of the filter. This way:
const checkQR = 'qrcode' in this.scanResponse;
const QR = this.scanResponse && this.scanResponse.qrcode ? this.scanResponse.qrcode.toLowerCase() : null;
const document_number = this.scanResponse && this.scanResponse.document_number ? this.scanResponse.document_number.toString().toLowerCase() : null;
const foundResult = this.visitors.filter((p: IVisitor) => {
let found = false;
if (checkQR) {
found = QR && p.code && p.code.toLowerCase() === QR;
} else {
found = document_number && p.document_number && p.document_number.toString().toLowerCase() === document_number;
}
return found;
});
Other possible techniques depend a lot on your actual code. You could read&filter "visitors" in chunks. You could move this filtering into backend and let the DB do it for you. You could make "visitor" models very tiny containing only code and document_number and then load only necessary models by filtered IDs from where you get it.
You could take the properties from this outside of the function to prevent checking an lowering for each cycle.
let dn = this.scanResponse && 'document_number' in this.scanResponse && this.scanResponse.document_number.toString().toLowerCase(),
qrCode = this.scanResponse && this.scanResponse.qrcode.toLowerCase();
const foundResult = this.visitors.filter((p: IVisitor) => {
return p.code.toLowerCase() === qrcode
|| p.document_number.toString().toLowerCase() === dn;
});

Run code after multiple methods resolve with Async and Await

I have three methods:
isSixCharactersLong(event) {
const input_len = event.target.value.length;
if (input_len === 6) {
this.setState({isSixCharactersLong: true})
} else {
this.setState({isSixCharactersLong: false})
}
}
isAlphanumeric(event) {
const input_str = event.target.value;
for (let i = 0; i < input_str.length; i++) {
const code = input_str.charCodeAt(i);
if (!(code > 47 && code < 58) && // numeric (0-9)
!(code > 64 && code < 91) && // upper alpha (A-Z)
!(code > 96 && code < 123)) { // lower alpha (a-z)
this.setState({isAlphanumeric: true});
} else {
this.setState({isAlphanumeric: false});
}
}
}
isEmpty(event) {
event.target.value ? this.setState({inputIsBlank: false}) : this.setState({inputIsBlank: true});
}
What I want to do is run a function after these three methods have resolved. So then I wrote the following:
async handleValidation(e) {
this.isAlphanumeric(e);
this.isEmpty(e);
this.isSixCharactersLong(e);
}
And then I have this final method that gets triggered by my React application.
handleOnChange = async (e) => {
await this.handleValidation(e)
.then(() => this.setState({code: e.target.value}))
};
I would think this will work, but I keep getting an error that e is null. Somehow, I lose the event.
What I believe the problem is, it's that I'm not using async and await on the correct methods.
You can reduce this code down to,
handleOnChange = (e) => {
const { value } = e.target
const isAlphanumeric = /^[a-z0-9]+$/i.test(value)
const isSixCharactersLong = value && value.length === 6
const inputIsBlank = !!value // or Boolean(value)
this.setState({ isAlphanumeric, isSixCharactersLong, inputIsBlank })
if (!inputIsBlank && isAlphanumeric && isSixCharactersLong)
this.setState({ code: value })
}
/^[a-z0-9]+$/i : Regular expression to test for alphaumerics case insensitively
!!: Type coercion to boolean i.e. if value is empty it will be falsy, the double negation turns it into true and back to false
Edit
As per the discussion in the comments, in order to set code only if the input is valid, I have added an if statement which essentially translates into, if the value is not blank (!inputIsBlank) and if the value is alphanumeric and if the input is six characters long then set code to value.
You are using async await in both functions when nothing is a promise, this is all synchronous code, so you actually don't need async await to solve this problem. maybe write your validation code to throw an error if something doesn't pass and then inside of handleOnChange you can run a ternary
handleOnChange = (e) => {
!this.handleValidation(e)? return :
this.setState({code: e.target.value}))
};

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;

Categories

Resources