I have the following JS code were i am displaying different status based on the response key from API. is there any better approach to optimise this code so that i don’t have to check each case with IF, in case if the number of status increases
if (data.shippingStatus) {
let shippingStatus = data.shippingStatus.toString();
if (shippingStatus === "AWAITING_SHIPMENT") {
shippingStatus = "Awaiting Shipment";
} else if (shippingStatus === "SHIPPED") {
shippingStatus = "Shipped";
} else if (shippingStatus === "DELIVERED") {
shippingStatus = "Delivered";
} else if (shippingStatus === "CANCELLED") {
shippingStatus = "Cancelled";
}
resData.push(setData(data.shippingStatus ? shippingStatus : ""));
}
Try object mapper:
const statusMapper: {[key:string]: string} = {
AWAITING_SHIPMENT: "Awaiting Shipment",
SHIPPED: "Shipped",
DELIVERED: "Delivered",
CANCELLED: "Cancelled"
};
if (data.shippingStatus) {
let shippingStatus = data.shippingStatus.toString();
resData.push(setData(data.shippingStatus ? statusMapper[shippingStatus] : ""));
}
EDIT: Added type to the mapper
There are different approaches to your question. If you're just looking for a solid solution for the current problem aka mapping values, you can either create an object mapper as the other answers suggest or just a simple function that formats your string e.g.:
var text = "AWAITING_SHIPMENT";
text = text.toLowerCase()
.split('_')
.map((s) => s.charAt(0).toUpperCase() + s.substring(1))
.join(' ');
console.log(text);
But if you're looking into the subject in a broader sense, you can use dynamic dispatch via polymorphism. This is an example that uses polymorphism to change behavior based on a type.
How my implementation works, it splits the status by _ and capitalize it, and finally return a new status, as required
// Dummy Data
const data = {
shippingStatus:"AWAITING_SHIPMENT"
}
// New Proposal
if(data.shippingStatus){
const { shippingStatus } = data;
const status =
shippingStatus.split('_')
.map(string => string.charAt(0).toUpperCase() + string.slice(1).toLowerCase())
.join(" ");
console.log(status)
// no need to check for data.shippingStatus twice
// while you're still within if condition, just do the following :
// resData.push(setDate(status))
}
Have map of possible statuses:
let statuses = {
'AWAITING_SHIPMENT': 'Awaiting Shipment',
'SHIPPED': 'Shipped',
...
};
resData.push(setData(data.shippingStatus ? statuses[shippingStatus] : ""));
Related
I have a function where I have to return for each "subcontractor" its response for each selection criteria.
Subcontractor object contains a selectionCriteria object. selectionCriteria object contains an array of data for each selectionCriteria a user has responded to.
Each array item is an object, that contains files, id, request (object that contains info about selection criteria user is responding to), response (contains value of the response).
Here is an example of how a subcontractor looks:
This is the function I come up with, but it's quite complex:
const { subcontractors } = useLoaderData<typeof loader>();
const { t } = useTranslation();
const submittedSubcontractors = subcontractors.filter(
(s) => s.status === 'submitted'
);
const subcontractorsResponsesToSelectionCriteria: Array<ISubcontractor> = [];
let providedAnswersResponded: boolean | null = null;
let providedAnswersFiles: Array<IFile> | [] = [];
let providedAnswersRequiresFiles: boolean | null = null;
submittedSubcontractors.forEach((u) => {
u.selectionCriteria.forEach((c) => {
if (c.request.id === criteriaId) {
if (c.response && 'answer' in c.response) {
if (typeof c.response.answer === 'boolean') {
providedAnswersResponded = c.response.answer;
} else {
providedAnswersResponded = null;
}
} else {
providedAnswersResponded = null;
}
providedAnswersFiles = c.files;
providedAnswersRequiresFiles = c.request.are_files_required;
subcontractorsResponsesToSelectionCriteria.push(u as ISubcontractor);
}
});
});
How could I simplify this code by using .reduce() method, or maybe even better ideas?
You should start working on reducing the level of nesting in your if/else like so:
function getProvidedAnswersResponded(response: any) {
if (response && ('answer' in response) && (typeof response.answer === 'boolean')) {
return response.answer;
}
return null;
}
submittedSubcontractors.forEach(u => {
u.selectionCriteria.forEach(c => {
if (c.request.id !== criteriaId) {
return;
}
providedAnswersResponded = getProvidedAnswersResponded(c.response);
providedAnswersFiles = c.files;
providedAnswersRequiresFiles = c.request.are_files_required;
subcontractorsResponsesToSelectionCriteria.push(u);
});
});
The strategy followed was basically to invert the special cases (such as c.requet.id === criteriaId) and exit the function immediately.
Also, extracting the "provided answer responded" function seems atomic enough to move it to a separate block, giving it more verbosity about what that specific code block is doing.
Can someone tell me how I can fix the "updated" issue. I can't update the items dynamically, I can only update manually.
Here is mode code
// Function (async) for rename the playlist and update
const renamePlaylist = async (el) => {
try {
const result = await AsyncStorage.getItem(STORAGE_KEY);
if (result !== null || result !== '') {
/* let filterArrayRenamePlaylist = playList.filter((val) => {
if (val.id !== el ) {
return val;
}
}); */
console.log(el, ' --------- el');
let renamePlayList = playList.filter((val) => {
if ( val.title === el ) {
let objIndex = playList.findIndex(obj => obj.title == val.title);
playList[objIndex].title = el; // it is not updated !
console.log(el , ' ----------- el 2');
};
});
// const updateList = [...renamePlayList ];
setUpdate(context, {addToPlayList: null, playList: [...renamePlayList] });
return await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(renamePlayList));
}
setRenamePlaylistModalVisible(false);
} catch (error) {
console.log(error)
}
}
When I try to manually update the playlist title like this:
let renamePlayList = playList.filter((val) => {
if ( val.title === el ) {
let objIndex = playList.findIndex(obj => obj.title == val.title);
playList[objIndex].title = 'This works'; // This works
// playList[objIndex].title = el; // This does not works
console.log(el , ' ----------- el 2');
};
});
I searched but couldn't find a solution to my problem.
lets take a closer look to your code and what your code is doing line by line, you made a few logical and code-style mistakes there:
let renamePlayList = playList.filter((val) => {
if ( val.title === el ) {
let objIndex = playList.findIndex(obj => obj.title == val.title);
playList[objIndex].title = el; // it is not updated !
console.log(el , ' ----------- el 2');
};
});
Issue 1:
Do not write misleading functions, filter function should not modify anything, nobody will expect it to do that, side-effects are bad.Please, consider to separate Filtering and Modifying logic
Issue 2:
Filter function should return a value, true or false. In your case it returns nothing, void, so in the snippet above your renamePlayList will be an empty array, [].
Issue 3: The logical issue itself.
You do not need to seek objIndex, because you have the val element from same array AND array filter function itself do provide index of currently processing element, take a look at the docs please: https://www.w3schools.com/jsref/jsref_filter.asp (Syntax section)
playList[objIndex] is absolutely equal to your val object on first matching val, if you have more elements with same title - your findIndex code will return the first found element only, all the rest elements will be unchanged
Your val.title === el statement and playList[objIndex].title = el; makes no sense due to title is el in any case, like title was el and you are reasigning it to el, if it was "abc" reasigning it to "abc" will not change anything. Your playList[objIndex].title = 'This works'; // This works works exactly because you are setting a different value.
Based on that i would recommend changing your filter function to
playList
.filter((val) => val.title === el)
.forEach((val) => val.title = "new title or whatever you need here, not el btw")
This code will modify elements in playList, not sure if it is expected but if not - just make a deep clone of it before processing.
your setUpdate will need a little change also after that:
setUpdate(context, {addToPlayList: null, playList: [...playlist] });
Please, double check what your code is doing and what are you trying to achieve. My guess is that your top level function needs 2 parameters, like oldTitle and newTitle so it would be
playList
.filter((val) => val.title === oldTitle)
.forEach((val) => val.title = newTitle)
I've been talking to my dev duck for the past few hours and cannot for the life of me rubber ducky debug this code. Basically, it returns [object Object] for a sub object in JSON. The juicy part is that if I copy and paste the logged raw JSON text before its parsed, and then parse it, it parses fine.
Heres the aforementioned code, with the values being fed in:
/*
We are feeding in:
{
'src' : './template.html',
'format' : 'plain',
'input' : {
'noun' : 'World'
},
'replace' : 'templateExampleSkeleton'
}
*/
// Locals.
let tmpScripts:Array<string> = [];
let tmpScriptIds:Array<string> = [];
let tmpStrings:Array<string> = [];
let tmpStringIds:Array<string> = [];
// Replace scripts with placeholder IDs, and store their contents in a temporary location.
// They will be restored later, because they would cause issues with the JSON parser.
// This isn't used in this case but is used in general.
args = args.replace(/js{{(.|\s)*}}/g, (substring:string) => {
let tmpScriptId:string = this.#utils.genRandomId(false, tmpScriptIds);
tmpScripts.push(substring.replace('js{{','').replace('}}',''));
return `%%{{${tmpScriptId}}}%%`;
})
// Replace 's with "s.
.replace(/'/gm, '"')
// Replace whitespace.
.replace(/(\s|\n|\t|\r)*/gm, '')
// Restore the strings using their IDs.
.replace(/##{{.{32}}}##/gm, (substring:string) => {
let tmpStringValue:string = '';
tmpStringIds.forEach((id:string, i:number) => {
if (substring.includes(id)) tmpStringValue = tmpStrings[i];
});
return tmpStringValue;
});
// Add curly brackets so that the JSON parser doesn't yell.
args = '{' + args + '}';
console.log(args); // ==> {"src":"./template.html","format":"plain","input":{"noun":"World"},"replace":"templateExampleSkeleton"}
// Parse the arguments as JSON.
let argsJson = JSON.parse(args);
// Using the new object, iterate through its keys in order to
// restore the scripts that were removed for parsing as JSON.
// This isn't(?) used in this case but is used in general.
Object.keys(argsJson).forEach((argKey, i) => {
argsJson[argKey] = argsJson[argKey].toString().replace(/%%{{.*}}%%/gm, (substring:string) => {
substring = substring.replace(/%%{{/, '').replace(/}}%%/, '');
let tmpScriptValue:string = '';
tmpScriptIds.forEach((id:string, i:number) => {
if (id === substring) tmpScriptValue = tmpScripts[i];
});
return tmpScriptValue;
});
});
// Log the object for debug.
console.log(argsJson); // ==> Object { src: "./template.html", format: "plain", input: "[object Object]", replace: "templateExampleSkeleton" }
Any help is very appreciated :^)
To close this question with an answer, as pointed out by #LexWebb:
Object.keys(argsJson).forEach((argKey, i) => {
argsJson[argKey] = argsJson[argKey].toString().replace(/%%{{.*}}%%/gm, (substring:string) => {
substring = substring.replace(/%%{{/, '').replace(/}}%%/, '');
let tmpScriptValue:string = '';
tmpScriptIds.forEach((id:string, i:number) => {
if (id === substring) tmpScriptValue = tmpScripts[i];
});
return tmpScriptValue;
});
});
Should be:
Object.keys(argsJson).forEach((argKey, i) => {
if (typeof argsJson[argKey] === 'string') {
argsJson[argKey] = argsJson[argKey].replace(/%%{{.*}}%%/gm, (substring:string) => {
substring = substring.replace(/%%{{/, '').replace(/}}%%/, '');
let tmpScriptValue:string = '';
tmpScriptIds.forEach((id:string, i:number) => {
if (id === substring) tmpScriptValue = tmpScripts[i];
});
return tmpScriptValue;
});
}
});
I have this function that is supposed to get referral codes from users. User gives a code and the referral code checked if it exists in the database then evaluated if
it does not match the current user, so that one should not refer himself and
it is a match with one of the codes in the database
This code however just does not find a match even if the code given is in the database. If the referral code matches the one of the current user, it works correctly and points that out i.e one cannot refer themselves.
But if the referral code is a match to that of another user which is how a referral system should work, it still says no match.
How can I remove this error
export const getID = functions.https.onCall(async(data, context) => {
const db = admin.firestore();
const usersSnapshot = await db.collection("user").get();
const allUIDs = usersSnapshot.docs.map(doc => doc.data().userID);
const userID = context.auth.uid;
const providedID = "cNx7IuY6rZlR9mYSfb1hY7ROFY2";
//db.collection("user").doc(providedID).collection("referrals").doc(userID);
await check();
function check() {
let result;
allUIDs.forEach(idFromDb => {
if (providedID === idFromDb && (idFromDb === userID)) {
result = "ownmatch";
} else if (providedID === idFromDb && (idFromDb !== userID)) {
result = "match";
} else {
result = "nomatch";
}
});
return result;
}
if (check() === "match") {
return {
message: `Match Found`,
};
} else if (check() === "ownmatch") {
return {
message: `Sorry, you can't use your own invite code`,
};
} else {
return {
message: `No User with that ID`
};
}
});
(This is not an answer, but a simple refactoring.)
This is what your code is currently doing (roughly, I didn't run it):
const resultMsgs = {
nomatch: 'No User With That ID',
ownmatch: 'Sorry, you can\'t use your own invite code',
match: 'Match Found',
}
function check(uids, providedId, userId) {
let result
uids.forEach(idFromDb => {
if (providedId !== idFromDb) {
result = 'nomatch'
return
}
if (userID === idFromDb) {
result = 'ownmatch'
return
}
result = 'match'
})
return result
}
export const getID = functions
.https
.onCall(async (data, context) => {
const userId = context.auth.uid
const providedId = 'cNx7IuY6rZlR9mYSfb1hY7ROFY2'
const db = admin.firestore()
const user = await db.collection('user').get()
const uids = user.docs.map(doc => doc.data().userId)
const checkResult = check(uids, providedId, userId)
return { message: resultMsgs[checkResult] }
})
(I removed the seemingly-spurious db collection operation.)
Your forEach is iterating over all of the uuids, but result will be set to whatever the last comparison was. Perhaps this is correct, but:
If you're looking for any match, this is not what you want.
If you're looking for all matches, this is not what you want.
If you're looking to match the last UUID, it's what you want, but an odd way to go about it.
So:
If you want any matches, use... ahem any form of an any function.
If you want all matches, use any form of an all function.
If you want the first match, then just check the first element.
If you want the complete set of comparisons then you'll need to use map instead of forEach, and handle each result appropriately, whatever that means in your case.
In any event, I'd recommend breaking up your code more cleanly. It'll be much easier to reason about, and fix.
Is there a way to make this if conditions look cleaner and easily to add more Query search in the future as in Open–closed principle?
For example:
if (event.queryParameters["name"]) {
result = await getResultByName(event.queryParameters["name"]);
} else if (event.queryParameters["emailAddress"]) {
result = await getResultByEmail(event.queryParameters["emailAddress"]);
} else if (event.queryParameters["param1"]) {
result = await getResultByParam1(event.queryParameters["param1"]);
} else if (event.queryParameters["something1"] && event.queryParameters["something2"]) {
result = await getResultBySomething(event.queryParameters["something1"], event.queryParameters["something2"]);
}
As you can see it look really messy.
Make a table of entries and use Array.prototype.find():
const lut = [
{ keys: ['name'], getResultBy: getResultByName },
{ keys: ['emailAddress'], getResultBy: getResultByEmail },
{ keys: ['param1'], getResultBy: getResultByParam1 },
{ keys: ['something1', 'something2'], getResultBy: getResultBySomething }
]
const params = event.queryParameters
const entry = lut.find(
({ keys }) => keys.every(key => key in params)
)
if (entry) {
const { keys, getResultBy } = entry
const result = await getResultBy(...keys.map(key => params[key]))
...
}
The problem with the original code is that it isn't DRY, and so any incremental modification will inevitably repeat what was already written.
Compare the following two incremental changes:
...
{ keys: ['fizz', 'buzz', 'fizzbuzz'], getResultBy: getResultByFizzBuzz }
...
else if (params.fizz && params.buzz && params.fizzbuzz) {
result = await getResultByFizzBuzz(params.fizz, params.buzz, params.fizzbuzz);
}
And tell me which one you'd rather be typing every time you go back and add a new function.
Since values are different and functions are different, there's not much place for improvement.
There's no necessity for bracket notation and there's no reason to reference event object every time.
It could be written as:
const { queryParameters } = event;
if (queryParameters.name) {
result = await getResultByName(queryParameters.name);
} else if ...
No other improvements can be made, unless the same case occurs in several places and could be DRYed up:
const paramHandlers = [
{ handler: getResultByName, paramNames: ['name'] },
...
];
Then paramHandlers can be iterated to check if paramNames match event.queryParameters properties.
So what you have looks perfectly readable, simple, and clean. You could create an event handler list if you need more flexibility:
eventHandlers = [nameHandler, emailHandler, ...];
var result;
for (var handler of eventHandlers) {
if (result = handler(event)) break;
}
In this example, the event handlers are functions that return a result if the event was consumed and processing should end. In your case your result can be a Promise or any arbitrary value.