function handleOrder(data) {
if (data.payment_type === 'VISA') {
handleVisaPayment()
}
if (data.payment_type === 'VISA') {
handleMastercardPayment()
}
if (data.payment_type === 'PayPal') {
handlePayPalPayment()
}
if (data.shipping === 'Express') {
handleExpressShipping()
}
if (data.shipping === 'Standard') {
handleStandardShipping()
}
}
Is there a better way to write this function especially by following best practices?
You can create an array data structure that will hold the value you want to compare and the function that you want to execute in that case. That will simplify your code and keep it manageable:
let allowedValues = [{
"value": "VISA",
"handler": handleVisaPayment
},
{
"value": "PayPal",
"handler": handlePayPalPayment
},
{
"value": "Express",
"handler": handleExpressShipping
},
{
"value": "Standard",
"handler": handleStandardShipping
}
]
function handleOrder(data) {
let matchedOrder = allowedValues.find(({
value
}) => value === data.payment_type);
if (matchedOrder) {
matchedOrder.handler();
}
}
You can even create a object mapping for those values and operations:
let allowedValues = {
"VISA": handleVisaPayment,
"PayPal": handlePayPalPayment,
"Express": handleExpressShipping,
"Standard": handleStandardShipping
}
function handleOrder(data) {
if (allowedValues[data.payment_type]) {
allowedValues[data.payment_type]();
}
}
First of all, you are executing the same IF statement twice in a row.
if (data.payment_type === 'VISA') {
handleVisaPayment
}
if (data.payment_type === 'VISA') {
handleMastercardPayment
}
I imagine it would be more logical if your second IF statement was like this:
if (data.payment_type === 'Mastercard') {
handleMastercardPayment
}
This looks like a simple copy-paste mistake, but I think it's worth pointing out.
Secondly, you might want to reorganize your code using a switch statement.
Ex:
switch (data.payment_type) {
case 'VISA':
handleVisaPayment
break;
case 'Mastercard':
handleMastercardPayment
break;
case 'PayPal':
handlePayPalPayment
break;
}
switch (data.shipping) {
case 'Express':
handleExpressShipping
break;
case 'Standard':
handleStandardShipping
break;
}
This should make your code easier to read/maintain/add new features in the future.
Again, I know it was specified to avoid using switch, but it does look like the most simple solution here.
I think this will give some building blocks to go further, as others have mentioned use mapping to resolve the functions you need.
const data = {
shipping: 'Standard',
payment_type: 'VISA',
}
const handleVisaPayment = () => console.log('handling visa payment')
const handleStandardShipping = () => console.log('handling standard shipping')
const orderHandlingMap = {
'Standard': handleStandardShipping,
'VISA': handleVisaPayment,
}
const handleOrder = data =>
Object.values(data)
.filter(element => orderHandlingMap[element])
.map(handlerName => orderHandlingMap[handlerName]())
handleOrder(data)
Related
I recently have spotted jscodeshift and leveraging it for refactoring.
But I can't find a way to add await before the node that I am seeking.
// AS-IS
userEvent.click(a);
await userEvent.click(b);
// TO-BE
await userEvent.click(a);
await userEvent.click(b);
This is how I query userEvent.click
const getFunctionCall = (obj:string, prop:string) => {
return source.find(j.CallExpression, {
callee: {
type: 'MemberExpression',
object: {
name: obj
},
property: {
name:prop
}
}
})
}
const clicks = getFunctionCall('userEvent', 'click');
clicks.forEach(i=> {
if (i.parentPath.value.type !== 'AwaitExpression') {
// code goes here
}
})
How can I add await before userEvent and let the rest code stay same in this case?
I checked out the document to find out how to build a statement or expression, but it wasn't understandable for me as I've just started to use this. I would appreciate if you can introduce any good materials for this library.
Thanks in advance!
It is simple, you need to wrap the existing CallExpression inside an await expression like this:
// Press ctrl+space for code completion
export default function transformer(file, api) {
const j = api.jscodeshift;
const root = j(file.source);
const body = root.get().value.program.body;
root.find(j.CallExpression, {
callee: {
object: {
name: "userEvent"
},
property: {
name: "click"
}
}
}).filter(path => {
return path.value.arguments[0].name === "a" ;
}).replaceWith(path => {
return j.awaitExpression(path.value)
});
return root.toSource();
}
I am making a CLI using inquirer in nodejs.
So in Every choice list I have to give Exit choice so if user want to exit he/she can easily Exit.
So I have to write Exit again and again to avoid that problem I made a Exit.js file and move Exit code there so I can use code again and again.
Exit.js
const executeQuery = require("../executeQuery");
function WantToExit() {
inquirer
.prompt([
{
name: "moreQuery",
type: "confirm",
message: "Want to do anything else?",
},
])
.then((answer) => {
if (answer.moreQuery) return executeQuery();
});
}
module.exports = WantToExit;
and My executeQuery Code look like this
ExecuteQuery.js
const wantToExit = require("../Exit");
const Science = require("../Science");
function executetQuery() {
inquirer
.prompt([
{
type: "list",
name: "cmsType",
message: " Select Subject Options ",
default: false,
choices: ["Science", "Maths", "English", "Exit"],
},
])
.then((answers) => {
if (answers.cmsType === "Science") {
Science();
} else if (answers.cmsType === "Exit") {
wantToExit();
}
});
}
module.exports = executetQuery;
when I select Exit from executeQuery option and press Y option I am getting this error from Exit.js file
if (answer.moreQuery) return executeQuery();
^
TypeError: executeQuery is not a function
at /home/admin/SchoolProject/src/Exit/index.js:13:36
Your approach has issues because it has produced a cyclic dependency of modules. You have "required" wantToExit in ExecuteQuery.js and also "required" executetQuery() in Exit.js
What I believe you want to achieve is to keep asking user his preferred subject and then do something based on his/her choice until a user selects Exit.
I would suggest to use a while loop in ExecuteQuery.js for the main prompt and use a boolean flag to check if user wants to exit.
const wantToExit = require("../Exit");
const Science = require("../Science");
function executetQuery() {
let toStop = false;
// use a while loop
while(!toStop) {
inquirer
.prompt([
{
type: "list",
name: "cmsType",
message: " Select Subject Options ",
default: false,
choices: ["Science", "Maths", "English", "Exit"],
},
])
.then(async (answers) => {
if (answers.cmsType === "Science") {
// you can also set toStop = true here if you want to
// stop after first iteration
Science();
} else if (answers.cmsType === "Exit") {
// wantToExit() now returns a boolean flag
toStop = await wantToExit();
}
});
}
}
module.exports = executetQuery;
and your Exit.js should be like
function WantToExit() {
inquirer
.prompt([
{
name: "moreQuery",
type: "confirm",
message: "Want to do anything else?",
},
])
.then((answer) => {
return !answer.moreQuery;
});
}
module.exports = WantToExit;
This is a scenario of circular dependency. A requires B, B requires A and so on. To get it working, you'll have to modify the module.exports.
In Exit.js file, change module.exports=WantToExit to module.exports.WantToExit = WantToExit and require it as const {WantToExit} =require('./Exit.js') in ExecuteQuery.js file.
Similiary, module.exports.ExecuteQuery=ExecuteQuery and require as const {ExecuteQuery} =require('./ExecuteQuery.js')
My guidance would be to learn RXJS and observables into this somehow.
Also i think (yield* ) might work in strict mode not sure, i wanted to not it because this is more a suggestion to play with and look into
Generator Functions* Exploring ES6 © 2015 - 2018 Axel Rauschmayer (cover by Fran Caye)
RXJS Guide Observable
const { Observable } = require("rxjs");
async function* wantToExit() {
(yield* await inquirer
.prompt([
{
name: "moreQuery",
type: "confirm",
message: "Want to do anything else?",
},
])
.then(answer => answer.moreQuery)
);
}
const executeQuery = new Observable(subscriber => {
inquirer.prompt([
{
type: "list",
name: "cmsType",
message: " Select Subject Options ",
default: false,
choices: ["Science", "Maths", "English", "Exit"],
},
]).then((answers) => {
if (answers.cmsType === "Science") {
subscriber.next(answers.cmsType);
} else if (answers.cmsType === "Exit") {
let doWeExit = await wantToExit().next();
if (doWeExit === ADD_SOMETHING_NO) {
executeQuery.subscribe(userResponse => userResponse);
} else {
console.log('Adios!');
return false;
}
}
});
});
module.exports = { executeQuery };
On a new page you could than do. Or you could just use it right under the function declaration. Hope this vaguely helps to the next step.
const {executeQuery} = require('{INCLUDE YOUR FILEPATH}');
executeQuery.subscribe(userResponse => {
if(userResponse === 'Science') science();
console.log(userResponse);
});
I have function that loops array and I have four if's - if it match I push value to output array:
const generate = (resources, resourceId) => {
let output = [];
for (let i = 0; i < resources.length; i++) {
if (resources[i].id === resourceId) {
if (CREATE & resources[i].privileges) {
output.push(CREATE);
}
if (READ & resources[i].privileges) {
output.push(READ);
}
if (UPDATE & resources[i].privileges) {
output.push(UPDATE);
}
if (DELETE & resources[i].privileges) {
output.push(DELETE);
}
}
}
return output;
};
I want to change this function to use map - is it possible? I try to do something like this:
const generateUsingMap = (resources, resourceId) => {
return resources.map((resource) => {
if (resource.id === resourceId) {
if (CREATE & resource.privileges) {
return CREATE;
}
if (READ & resource.privileges) {
return READ;
}
if (UPDATE & resource.privileges) {
return UPDATE;
}
if (UPDATE & resource.privileges) {
return UPDATE;
}
}
});
};
But in this case I will have only one value, because it returns from first if.
Maybe I need to use another function? I don't want to use for or forEach because in that cases I need to create unnecessary variable.
Update
My function is working in loop, function receive 2 arguments resources and resourceId.
For example variable resources contains:
{
"id": "1",
"name": "Test name 1",
"privileges": 1
},
{
"id": "2",
"name": "Test name 2",
"privileges": 2
},
{
"id": "3",
"name": "Test name 3",
"privileges": 8
},
{
"id": "4",
"name": "Test name 4",
"privileges": 0
},
{
"id": "5",
"name": "Test name 5",
"privileges": 15
}
]
Variable resourceId contains number (id) and receive severally values, for example on first iteration 1, for second 2 and so on.
For resources from example expected output will be:
[1]
[2]
[8]
[]
[1,2,4,8]
You can use reduce if both resource object id and privileges do not exist do not check further just return what you already accrued.
Only if both are present then check the CRUD operations.
const result = resources.reduce((output) => {
if (resources[i].id !== resourceId && !resources[i].privileges) {
return output;
}
if (CREATE) {
output.push(CREATE);
}
if (READ) {
output.push(READ);
}
if (UPDATE) {
output.push(UPDATE);
}
if (DELETE) {
output.push(DELETE);
}
return output;
}, [])
const generateUsingMap = (resources, resourceId) => {
return resources.filter(resource => resource.id === resourceId)
.map((resource) => {
if (CREATE & resource.privileges) {
return CREATE;
}
if (READ & resource.privileges) {
return READ;
}
if (UPDATE & resource.privileges) {
return UPDATE;
}
if (UPDATE & resource.privileges) {
return UPDATE;
}
});
};
Create an empty array resultArr. Iterate through resources via forEach and append options to it. Return the array at the end of the function
const generateUsingMap = (resources, resourceId) => {
const resultArr = [];
resources.forEach((resource) => {
if (resource.id === resourceId && resource.privileges) {
if (CREATE) {
resultArr.push(CREATE);
}
else if (READ) {
resultArr.push(READ);
}
else if (UPDATE) {
resultArr.push(UPDATE);
}
else if (DELETE) {
resultArr.push(DELETE);
}
}
});
return resultArr;
};
I have read few articles about this topic and few SO questions like this one but nothing really fit to my situation.
So basically I'm creating simple one-page app almost without any javascript involved. Very only place where I need JS is this translation. Generally it's not a problem for me to create some script in JS to translate so I did like this:
I'm importing two files into my index.html:
<script src="js/text.js"></script>
<script src="js/translator.js"></script>
In text.js I have constant object containing texts for website to display:
// Global constant "text" accessible in translator.js file
const text = {
PL: {
aboutHeading: "Kilka słów o mnie"
},
ENG: {
aboutHeading: "Few words about me"
}
};
In translator.js I have object responsible for checking which language and filling divs / headings / whatever of text:
// global translator object
const translator = {
currentLanguage: '',
checkLanguage() {
switch (window.navigator.language) {
case 'pl':
case 'pl-PL': {
this.currentLanguage = 'pl';
break;
}
case 'en': {
this.currentLanguage = 'eng';
break;
}
default: {
this.currentLanguage = 'eng';
break;
}
}
//alert(this.currentLanguage);
//alert(window.navigator.language);
},
fillText(lang) {
if(lang === 'pl') {
document.getElementById('few-words-about-me-header').innerHTML = text.PL.aboutHeading;
alert('inserted pl');
}
if(lang === 'eng') {
document.getElementById('few-words-about-me-header').innerHTML = text.ENG.aboutHeading;
alert('inserted eng');
}
},
};
translator.checkLanguage();
translator.fillText(translator.currentLanguage);
document.getElementById('polish-flag').addEventListener('click', () => {
translator.fillText('pl');
});
document.getElementById('english-flag').addEventListener('click', () => {
translator.fillText('eng');
});
Generally everything is working as expected, the only thing I'm worried about is that I've got two global variables here and I'm not really sure what to do about it. I feel like I can do this translation in some more efficient, prettier way.
So questions are - if it's okey to have this two global variables and if I can achieve same as above code in some prettier way?
Simply encapsulate both in an IIFE in a single script so that nothing pollutes the global namespace.
// main.js
(() => {
const text = {
PL: {
aboutHeading: "Kilka słów o mnie"
},
ENG: {
aboutHeading: "Few words about me"
}
};
const translator = {
currentLanguage: '',
checkLanguage() {
switch (window.navigator.language) {
case 'pl':
case 'pl-PL':
{
this.currentLanguage = 'p';
break;
}
case 'en':
{
this.currentLanguage = 'eng';
break;
}
default:
{
this.currentLanguage = 'eng';
break;
}
}
//alert(this.currentLanguage);
//alert(window.navigator.language);
},
fillText(lang) {
if (lang === 'pl') {
document.getElementById('few-words-about-me-header').innerHTML = text.PL.aboutHeading;
alert('inserted pl');
}
if (lang === 'eng') {
document.getElementById('few-words-about-me-header').innerHTML = text.ENG.aboutHeading;
alert('inserted eng');
}
},
};
translator.checkLanguage();
translator.fillText(translator.currentLanguage);
document.getElementById('polish-flag').addEventListener('click', () => {
translator.fillText('pl');
});
document.getElementById('english-flag').addEventListener('click', () => {
translator.fillText('eng');
});
})();
If you have to create the text dynamically for some reason, you can avoid creating a global variable there by using <script type="application/json"> instead, which is parseable but not automatically added to the global namespace. For example:
<div></div>
<script type="application/json">{"foo": "foo", "bar": "bar"}</script>
<script>
(() => {
const text = JSON.parse(document.querySelector('script[type="application/json"]').textContent);
document.querySelector('div').textContent = text.foo + ' / ' + text.bar;
})();
</script>
You can also use object lookups to simplify the currentLanguage setting. switch statements are often too wordy and error-prone, compared to the alternative:
checkLanguage() {
const languages = {
pl: ['pl', 'pl-PL'],
en: ['eng'],
};
const navigatorLanguage = window.navigator.language;
const foundLanguageObj = Object.entries(languages)
.find(([setting, arr]) => arr.includes(navigatorLanguage));
this.currentLanguage = foundLanguageObj ? foundLanguageObj[0] : 'eng';
}
I have two filters in my custom panel in Kibana3:
request = request.query(
$scope.ejs.FilteredQuery(
boolQuery,
filterSrv.getBoolFilter(filterSrv.ids()) // has to be merged with BoolFilter2
))
.size($scope.panel.size);
This works for either of the two filters but I want to filter with the two objects but I dont know how to merge them. Can anyone help?
Kibana 3 filters can be set to the "either" mode instead of "must".
This translates into filters that look like this:
{
"filter": {
"bool": {
"should": [
{
"fquery": {
"query": {
"query_string": {
"query": "test1"
}
},
"_cache": true
}
},
{
"fquery": {
"query": {
"query_string": {
"query": "test2"
}
},
"_cache": true
}
}
]
}
}
}
Here is the filterSrv code that handles that in Kibana 3: https://github.com/elasticsearch/kibana/blob/3.0/src/app/services/filterSrv.js#L144
this.getBoolFilter = function(ids) {
var bool = ejs.BoolFilter();
// there is no way to introspect the BoolFilter and find out if it has a filter. We must keep note.
var added_a_filter = false;
_.each(ids,function(id) {
if(dashboard.current.services.filter.list[id].active) {
added_a_filter = true;
switch(dashboard.current.services.filter.list[id].mandate)
{
case 'mustNot':
bool.mustNot(self.getEjsObj(id));
break;
case 'either':
bool.should(self.getEjsObj(id));
break;
default:
bool.must(self.getEjsObj(id));
}
}
});
// add a match filter so we'd get some data
if (!added_a_filter) {
bool.must(ejs.MatchAllFilter());
}
return bool;
};
this.getEjsObj = function(id) {
return self.toEjsObj(dashboard.current.services.filter.list[id]);
};
You could modify that function to take an optional argument that sets the mandate to either.