Refactor this code into something modular - javascript

I have the following JS code
addSelectorLabel = (params) => {
let valEntidad = params.find(tipo => { if (tipo.name === "type") return tipo })?.value || ""
let valCNAE = params.find(tipo => { if (tipo.name === "cnae") return tipo })?.value || ""
let valProvince = params.find(tipo => { if (tipo.name === "province") return tipo })?.value || ""
let valVia = params.find(tipo => { if (tipo.name === "address_prefix") return tipo })?.value || ""
if (valEntidad !== "") {
let labelSelector = EntidadArr.find(tipo => {
if (tipo.cod === valEntidad) {
return tipo
}
})?.val || ""
if (labelSelector !== "") {
params.push(
{
name: "typeLabel", value: labelSelector, hasError: false
}
)
}
}
if (valCNAE !== "") {
let labelSelector = CNAE.find(tipo => {
if (tipo.cod === valCNAE) {
return tipo
}
})?.val || ""
if (labelSelector !== "") {
params.push(
{
name: "cnaeLabel", value: labelSelector, hasError: false
}
)
}
}
if (valProvince !== "") {
let labelSelector = ProvinciaArr.find(tipo => {
if (tipo.cod === valProvince) {
return tipo
}
})?.val || ""
if (labelSelector !== "") {
params.push(
{
name: "provinceLabel", value: labelSelector, hasError: false
}
)
}
}
if (valVia !== "") {
let labelSelector = ViaArr.find(tipo => {
if (tipo.cod === valVia) {
return tipo
}
})?.val || ""
if (labelSelector !== "") {
params.push(
{
name: "viaLabel", value: labelSelector, hasError: false
}
)
}
}
return params
}
The same process is repeated 4 times, so im trying to create a modular function to make it shorter.
But im not really sure where to start
Im looking to create something like for the if sentences
export const selectLabel = async (jsonToCrawl, valueToFind, params) => {
let labelSelector = jsonToCrawl.find(tipo => {
if (tipo.cod === valueToFind) {
return tipo
}
})?.val || ""
if (labelSelector !== "") {
params.push(
{
name: "typeLabel", value: labelSelector, hasError: false
}
)
return params
}
}
//call it like --> params = selectLabel(EntidadArr , valEntidad, params)
But im wondering. what happens with the params.push ? Do i have to return it? and reset params?
Im not sure how to apprach this

When you're doing something like this, what you want to look for is what changes in the different copies of the code. Then you make a function that accepts the things that change as parameters, and uses those parameter values to do the work. This called parameterizing the code.
For instance, let's look just at the initial part:
let valEntidad = params.find(tipo => { if (tipo.name === "type") return tipo })?.value || ""
let valCNAE = params.find(tipo => { if (tipo.name === "cnae") return tipo })?.value || ""
let valProvince = params.find(tipo => { if (tipo.name === "province") return tipo })?.value || ""
let valVia = params.find(tipo => { if (tipo.name === "address_prefix") return tipo })?.value || ""
All four find calls above do the same thing, the only thing that varies is the name that the code checks against.
That means you can write a function accepting that name and have it do the work:
function findTypeValueByName(types, name) {
return types.find(tipo => { if (tipo.name === name) return tipo })?.value || "";
}
Then you use that function:
let valEntidad = findTypeValueByName(params, "type");
let valCNAE = findTypeValueByName(params, "cnae");
let valProvince = findTypeValueByName(params, "province");
let valVia = findTypeValueByName(params, "address_prefix");
You can apply the same process to the four if statements. They have more things that vary, so there will be more than one parameter to use, but the same approach will work for them as well.
Side note: The callback for find is supposed to return a flag (true/false) for whether the entry is the one you want. So instead of:
function findTypeValueByName(types, name) {
return types.find(tipo => { if (tipo.name === name) return tipo })?.value || "";
}
just return the result of the comparison:
function findTypeValueByName(types, name) {
return types.find(tipo => tipo.name === name)?.value || "";
}
That does the same thing. Your original code does work, but the reason it works is a by-product of the fact that your callback wasn't returning anything (so implicitly returns undefined) in the false case, and was returning an object in the true case. That happens to work, but it's best to clearly return a flag instead.

Related

can't check if variable is undefined in typescript

I'm trying to solve compiler error " Object is possibly 'undefined' "
const destinationColumnIndex = (): number => {
if (typeof result.destination === 'undefined') {
return 0;
}
return boardData.findIndex(
(column) => column.id === Number(result.destination.droppableId)
);
};
but typescript compiler still tells me that "result.destination" may be undefined.
I have tried also:
if (result.destination === undefined) {
return 0;
}
and:
if (!result.destination) {
return 0;
}
and:
if (!result || typeof result.destination === 'undefined') {
return 0;
}
and nothing works. Even thought it may be some bug so i restarted VS Code but there are still the same error.
EDIT - MORE CODE:
const onDragEnd = async (result: DropResult) => {
if (!result.destination) {
return;
}
const sourceColumnIndex = (): number =>
boardData.findIndex(
(column) => column.id === Number(result.source.droppableId)
);
const destinationColumnIndex = (): number => {
if (typeof result === 'undefined' || result.destination === undefined) {
return 0;
}
return boardData.findIndex(
(column) => column.id === Number(result.destination.droppableId)
);
};
it's function inside of react component
You should just do:
if (result === undefined || result?.destination === undefined) {
return 0;
}
Checking typeof is not a good way to check for undefined.
or
if (!result || result?.destination === undefined) {
return 0;
}
UPDATE
try this:
const onDragEnd = (result: DropResult) => {
if (!result || !result.destination) {
return;
}
const sourceColumnIndex = (): number =>
boardData.findIndex(
(column) => column.id === Number(result.source?.droppableId)
);
const destinationColumnIndex = (): number => {
if (!result || !result.destination) {
return 0;
}
return boardData.findIndex(
(column) => column.id === Number(result.destination?.droppableId)
);
};
}

How to refactor the if else code using typescript and react?

i want to refactor the below ifelse code
below is my code,
const toggle = React.useCallback(
async (itemId: string) => {
if (isDrawing && editItemId === itemId) {
cancelDrawing();
} else if (isDrawing) {
cancel();
itemId && startDrawing(itemId);
} else {
itemId && startDrawing(itemId);
}
}
);
could someone help me with this. thanks.
if(isDrawing || editItemId === itemID) {
cancelDrawing();
itemId && startDrawing(itemId);
} else {
itemId && startDrawing(itemId);
}
It looks you call cancelDrawing only when isDrawing is truthy?
const toggleDrawing = React.useCallback(
async (itemId: string | null) => {
isDrawing && cancelDrawing();
itemId && (editItemId !== itemId) && startDrawing(itemId);
}
);

How to sort strings and numbers in a same array for a datatable

I am rebuilding a datatable using React with JavaScript so I am sorting some information that includes strings and numbers so I have been trying to implement array.sort(a, b) => a.toLowerCase > b.toLoweCase... etc to sort everything, and at some point it works perfectly until I click the button that sorts the numbers because It gives me an error,
And without tolowercase function that specific sort number section works perfectly but the other ones that have strings with uppercase and lowercase don't sort correctly
How can I solve this problem?
const useSortableData = (data, config = null) => {
const [sortConfig, setSortConfig] = useState(config);
const sortedItems = useMemo(() => {
let sortableItems = [...data];
if (sortConfig !== null) {
sortableItems.sort((a, b) => {
const optionA = a[sortConfig.key].toString().toLowerCase()
const optionB = b[sortConfig.key].toString().toLowerCase()
if (optionA < optionB) {
return sortConfig.direction === 'ascending' ? -1 : 1;
}
if (optionA > optionB) {
return sortConfig.direction === 'ascending' ? 1 : -1;
}
return 0;
});
}
return sortableItems;
}, [data, sortConfig]);
const requestSort = (key) => {
let direction = 'ascending';
if (
sortConfig &&
sortConfig.key === key &&
sortConfig.direction === 'ascending'
) {
direction = 'descending';
}
setSortConfig({ key, direction });
};
return { data: sortedItems, requestSort, sortConfig };
};
Error which I receive when I click the number sort section
TypeError: a[sortConfig.key].toLowerCase is not a function
More clean code
const optionA = a[sortConfig.key].toString().toLowerCase()
const optionB = b[sortConfig.key].toString().toLowerCase()
if (optionA < optionB) {
return sortConfig.direction === 'ascending' ? -1 : 1;
}
if (optionA > optionB) {
return sortConfig.direction === 'ascending' ? 1 : -1;
}
return 0;
Continuing from comment for sorting alphanumerical data:
Hope you don't mind me cleaning up a little
const sortedItems = useMemo(() => {
let sortableItems = [...data];
if (sortConfig !== null) {
sortableItems.sort((a, b) => {
let aString = a.toString().toLowerCase();
let bString = b.toString().toLowerCase();
if (sortConfig.direction === 'descending') {
return bString.localeCompare(aString, 'en', { numeric: true })
}
// return default as ascending
return aString.localeCompare(bString, 'en', { numeric: true })
});
}
return sortableItems;
}, [data, sortConfig]);

javascript array filter very slow in REACT

I have a custom listbox, a div that contains a vertical list of other div children. And I have input for search something else in the list. It's working but in large data, it's working very slowly.
Also search criterion produce dynamically with column chooser. How can i increase search performance.
Firsly, prepare filter data for search and keeping state on the page load
prepareFilterData(allData) {
const filteredData = [];
let columnChooser = JSON.parse(getItemFromLocalStorage("ColumnData"));
allData.map(item => {
var data = "";
columnChooser.map(element => {
var newData = { value: item[element.value], format: element.format };
var filterItem = getFilterDataFormat(newData);
data += filterItem + " ";
});
filteredData.push(data);
});
this.setState({
filteredData: filteredData
});
}
Secondly, When user enter an char to textbox, i'm checking filteredData
filterList() {
const updatedList = this.state.allData.length > 0 ? this.state.allData : [];
var filteredData = [];
filteredData = updatedList.filter((item, index) => {
const data = this.state.filteredData[index];
return data.indexOf(this.state.searchInputValue) !== -1;
});
return filteredData;
}
This is input statement
<input
id="searchBox"
type="text"
className="filter-input empty"
placeholder="Search"
onChange={this.filterList}
value={this.props.state.searchInputValue}
style={{ width: "100%" }} />
Using a standard for loop can significantly increase the performance, especially in your case where you're using indexOf which is causing another iteration in your filter. The filter operation uses callbacks and it's often used because of the simpler syntax but it's these callback that make the operation to be slower especially on big data.
Read more here.
I found the solution.
SOLUTION:
I create a util.js in my project, and I called createFilter function.
import Fuse from "fuse.js";
import { toTrLowerCase } from "./process";
function flatten(array) {
return array.reduce((flat, toFlatten) => flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten), []);
}
export function getValuesForKey(key, item) {
const keys = key.split(".");
let results = [item];
keys.forEach(_key => {
const tmp = [];
results.forEach(result => {
if (result) {
if (result instanceof Array) {
const index = parseInt(_key, 10);
if (!isNaN(index)) {
return tmp.push(result[index]);
}
result.forEach(res => {
tmp.push(res[_key]);
});
} else if (result && typeof result.get === "function") {
tmp.push(result.get(_key));
} else {
tmp.push(result[_key]);
}
}
});
results = tmp;
});
// Support arrays and Immutable lists.
results = results.map(r => (r && r.push && r.toArray ? r.toArray() : r));
results = flatten(results);
return results.filter(r => typeof r === "string" || typeof r === "number");
}
export function searchStrings(strings, term, { caseSensitive, fuzzy, sortResults, exactMatch } = {}) {
strings = strings.map(e => e.toString());
try {
if (fuzzy) {
if (typeof strings.toJS === "function") {
strings = strings.toJS();
}
const fuse = new Fuse(
strings.map(s => {
return { id: s };
}),
{ keys: ["id"], id: "id", caseSensitive, shouldSort: sortResults }
);
return fuse.search(term).length;
}
return strings.some(value => {
try {
if (!caseSensitive) {
value = value.toLowerCase();
}
if (exactMatch) {
term = new RegExp("^" + term + "$", "i");
}
if (value && value.search(term) !== -1) {
return true;
}
return false;
} catch (e) {
return false;
}
});
} catch (e) {
return false;
}
}
export function createFilter(term, keys, options = { caseSensitive: false, fuzzy: false, sortResults: false, exactMatch: false }) {
debugger;
return item => {
if (term === "") {
return true;
}
if (!options.caseSensitive) {
term = term.toLowerCase();
}
const terms = term.split(" ");
if (!keys) {
return terms.every(term => searchStrings([item], term, options));
}
if (typeof keys === "string") {
keys = [keys];
}
return terms.every(term => {
// allow search in specific fields with the syntax `field:search`
let currentKeys;
if (term.indexOf(":") !== -1) {
const searchedField = term.split(":")[0];
term = term.split(":")[1];
currentKeys = keys.filter(key => key.toLowerCase().indexOf(searchedField) > -1);
} else {
currentKeys = keys;
}
return currentKeys.some(key => {
const values = getValuesForKey(key, item);
values[0] = toTrLowerCase(values[0]);
return searchStrings(values, term, options);
});
});
};
}
And then i added fuse.js to package.json.
"fuse.js": "^3.0.0"
I called createFilter function like that... term is searching value key
keysToFilter is which array column you wanna search.
this.state.allData.filter(createFilter(term, this.state.keysToFilter));
Link: https://github.com/enkidevs/react-search-input

Nodejs - Express - Best practice to handle optional query-string parameters in API

I want to make a API which have 5 optional query parameters, I want to know if there is a better way to handle this, right now I check each one of them with if conditions, which is kind of dirty! is there any way that I can handle all scenarios without using lot's of if conditions?
let songName = req.query.songName
let singerName = req.query.singerName
let albumName = req.query.albumName
let publishDate = req.query.publishDate
if(songName && singerName && albumName && publishDate) {
const response = songs.filter(c => {
return c.songName === songName && c.singerName === singerName && c.albumName === albumName && c.publishDate === publishDate
}
res.send({
"Data" : response
})
}
if(songName && singerName && albumName && !publishDate) {
const response = songs.filter(c => {
return c.songName === songName && c.singerName === singerName && c.albumName === albumName
}
res.send({
"Data" : response
})
}
if(songName && singerName && !albumName && publishDate) {
const response = songs.filter(c => {
return c.songName === songName && c.singerName === singerName && c.publishDate === publishDate
}
res.send({
"Data" : response
})
}
if(songName && !singerName && albumName && publishDate) {
const response = songs.filter(c => {
return c.songName === songName && c.albumName === albumName && c.publishDate === publishDate
}
res.send({
"Data" : response
})
}
if(!songName && singerName && albumName && publishDate) {
const response = songs.filter(c => {
return c.singerName === singerName && c.albumName === albumName && c.publishDate === publishDate
}
res.send({
"Data" : response
})
}
.
.
.
You could use the ternary operator to do this all in one query. If the parameter is defined you check for equality and else you just return true. This could look like this:
const response = songs.filter(c => {
return (songName ? (c.songName === songName) : true) &&
(singerName ? (c.singerName === singerName) : true) &&
(albumName ? (c.albumName === albumName) : true);
});
res.send({
"Data": response
})
I may find Lodash to be useful for this one:
const response = songs.filter(song => {
return _.isEqual(req.query, _.pick(song, Object.keys(req.query)))
})
I suggest you to use Joi
It is very powerful library for javascript validations. You can make even conditional validations using it. See the complete docs.
I created basic schema for your scenario here.
// validation
const schema = Joi.object().keys({
songName: Joi.string()
singerName: Joi.string()
albumName: Joi.string()
publishDate: Joi.date()
});
const { error, value } = Joi.validate(req.query, schema, { abortEarly: false, allowUnknown: false });
if (error !== null) return res.send(400, { code: 400, message: "validation error", error: error.details });
It is easier to read and understand for other developers too. You can standardized the validations in the overall project.

Categories