How to optimize filter JS? - javascript

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

Related

More Elegant Solution To Fetching a Selector From Within Two IFrames

I've been working on a webscraper that uses puppeteer. The scraper, as shown below, aims to click on a button that is inside of two iframes, although the website that I'm working with generates the content of the iframes with a bunch of javascript and it can take upwards of 8 seconds for the button to render(during which there are also a number of open connections between server and client, so page.waitForNetworkIdle() is out of the question). The code here actually works; however, I'm sure of the existence of some significantly more elegant solution to this problem, most likely not involving async functions. I tried using a couple of promise-chaining patterns earlier but nothing seemed to work. If anyone could point me to some method for solving this problem with less jank, I'd be grateful.
const t = 5000;
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
//--------------------------------------------------//
var tFrame = await page.waitForFrame(async fr => {
return fr.name().length === 5
})
var ebRealModal = tFrame.childFrames().find(fra => {return fra.name() === 'ebRealModal'});
var search = null
while(search === null || search === undefined){
tFrame = page.frames().find(fra => {return fra.name().length === 5});
if(tFrame !== null && tFrame !== undefined){
ebRealModal = tFrame.childFrames().find(fra => {return fra.name() === 'ebRealModal'});
if(ebRealModal !== null && ebRealModal !== undefined){
search = await ebRealModal.$("#search")
}
}
await delay(t)
}
button = await ebRealModal.$("#search");
await button.evaluate(b => b.click());

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

Using for-of / for each array of states to manipulate dom

I was wondering how I would automate the below in a kind of for-of way.
If I want to perform the same test and action for many specific states (not all), how would I go about doing something like
var anArray = [firstName, lastName];
for (anArray as Q){
if(this.state.Q.length > 1 || this.state.Q.length == 0){
document.getElementById(toString(Q)).className="someClass";
}else{
document.getElementById(toString(Q)).className="AnOtherClass";
}
}
in this return:
return function(e) {
var state = {};
state[key] = e.target.value;
this.setState(state, () => {
if(validate(this.state.dob)){
document.getElementById("dob").className="inputYay";
}else{
document.getElementById("dob").className="inputErr";
}
//firstName
if(this.state.firstName.length > 1 || this.state.firstName.length == 0){
document.getElementById("firstName").className="inputYay";
}else{
document.getElementById("firstName").className="inputErr";
}
//lastName
if(this.state.lastName.length > 1 || this.state.lastName.length == 0){
document.getElementById("lastName").className="inputYay";
}else{
document.getElementById("lastName").className="inputErr";
}
}
);
}
Ultimately I would like only to check the key of each state provided by the return function, but I cant get my head around how that would work as I am validating different types of responses.
I would greatly appreciate either method for my understanding.
many thanks as always :

Can't seem to move segment into repeating field

I have a piece of code that I'm trying to get to work on an interface. Basically we take some fields and drop into other segments. The problem seems to be that it leaves the data where it is instead of moving it to the indexed PID segment. Also the CP variable is returning 'undefined' for some reason.
var i = msg['PID']['PID.13'].length();
var homeNum;
var netNum;
var cpNum;
while(i--)
{
if (msg['PID']['PID.13'][i]['PID.13.2'].toString() == "PRN")
{
homeNum = msg['PID']['PID.13'][i]['PID.13.9'];
}
if (msg['PID']['PID.13'][i]['PID.13.2'].toString() == "NET")
{
netNum = msg['PID']['PID.13'][i]['PID.13.4'];
}
if (msg['PID']['PID.13'][i]['PID.13.2'].toString() == "CP")
{
cpNum = msg['PID']['PID.13'][i]['PID.13.9'];
}
msg['PID']['PID.13'][i] = "";
}
msg['PID']['PID.13'][0]['PID.13.1'] = homeNum;
msg['PID']['PID.13'][0]['PID.13.4'] = netNum;
msg['PID']['PID.13'][1]['PID.13.1'] = cpNum;
Sample HL7 msg I am using before transforms (from our test system, NOT live data)
It should resemble this instead:
|9999999999^^^test#test.com~99999999999~~~|
Any ideas/pointers on why it's not moving?
You are missing a toString() when you set the variables. A typical Mirth thing, because you get the E4X object back in the variable instead of the value you expected.
In addition to this, you should check the variables for undefined values before setting them on the new structure because otherwise you end up with "undefined" in the fields.
This is a working solution:
var i = msg['PID']['PID.13'].length();
var homeNum;
var netNum;
var cpNum;
while(i--)
{
if (msg['PID']['PID.13'][i]['PID.13.2'].toString() == "PRN")
{
homeNum = msg['PID']['PID.13'][i]['PID.13.9'].toString();
}
if (msg['PID']['PID.13'][i]['PID.13.2'].toString() == "NET")
{
netNum = msg['PID']['PID.13'][i]['PID.13.4'].toString();
}
if (msg['PID']['PID.13'][i]['PID.13.2'].toString() == "CP")
{
cpNum = msg['PID']['PID.13'][i]['PID.13.9'].toString();
}
msg['PID']['PID.13'][i] = "";
}
if(homeNum != null) msg['PID']['PID.13'][0]['PID.13.1'] = homeNum;
if(netNum != null) msg['PID']['PID.13'][0]['PID.13.4'] = netNum;
if(cpNum != null) msg['PID']['PID.13'][1]['PID.13.1'] = cpNum;

Categories

Resources