Dynamically create a links from an array of items - javascript

I need to create a ul of links based on the valued in an array. The links are created but the href attribute is being randomly assigned and doesn't correspond to the actual value I'm passing in to my getUrl function.
Here is my code so far:
getURL (type) {
let url
switch (type) {
case 'Yahoo':
url = 'https://www.yahoo.com/'
case 'Bing':
url = 'https://www.bing.com/'
default:
url = 'https://www.google.com'
}
return url
}
and the render:
render () {
let { expandList } = this.state
let listItems = [
'Google',
'Bing',
'Yahoo'
]
let list = expandList
? listItems.map((item, index) => {
return (
<li key={index}>
<a href={this.getURL(item)} target='_blank'>
{item}
</a>
</li>
)
})
: ''
return (
<div>
<ul>{list}</ul>
</div>
)
}

You need to break after each case or return from it. If you don't it will always continue to default, and return the google url.
function getURL(type) {
switch (type) {
case 'Yahoo':
return 'https://www.yahoo.com/'
case 'Bing':
return 'https://www.bing.com/'
default:
return 'https://www.google.com'
}
}
console.log(getURL('Yahoo'));
console.log(getURL('Bing'));

switch basically tries to match type with the argument to the case, and when there's a match, it will execute the codes in all the cases below it unless you explicitly break it.
default is a catch-all in case none of the cases match the type.
The correct way to do it with switch would be breaking out of it after the case statement is executed. One way of doing this is by inserting break:
getURL (type) {
let url
switch (type) {
case 'Yahoo':
url = 'https://www.yahoo.com/'
break
case 'Bing':
url = 'https://www.bing.com/'
break
default:
url = 'https://www.google.com'
}
return url
}
You can also definitely return the url in the case statement as Ori Drori suggested, which will not only break out of the switch, but also break out of the function.
I would also recommend ending your statements with a semicolon. Even though it's optional, it can prevent unexpected errors.
Also, I would recommend lowercasing type with type.tolowercase() and match it with lowercase strings.
If all getURL does is to map the name of the website to the url, an alternative with less code would be using an object:
var nameToUrlMap = {
'yahoo': 'https://www.yahoo.com/',
'bing': 'https://www.bing.com/',
};
and then use it like:
var type = 'Yahoo';
var lowercasedType = type.tolowercase();
var url = nameToUrlMap[lowercasedType] || 'https://www.google.com/'; // falls back to google if name doesn't exist in nameToUrlMap
console.log(url); // "https://www.yahoo.com/"
console.log(nameToUrlMap['abcd'] || 'https://www.google.com/'); // "https://www.yahoo.com/"
You can wrap it in a function if you want:
var nameToUrlMap = {
'yahoo': 'https://www.yahoo.com/',
'bing': 'https://www.bing.com/',
};
function getURL(type) {
var lowercasedType = type.tolowercase();
var url = nameToUrlMap[lowercasedType] || 'https://www.google.com/'; // falls back to google if name doesn't exist in nameToUrlMap
}
You can use const or let in place of var as well.

Related

Using object literals for my complex cases

Hey I was wondering if it is somehow possible to use a object literal in this case to clean up my switch statement.
export const getTravelInfo = () => {
const url = window.location.href;
switch (url as any) {
case url.includes('trips'):
return epicTrips;
case url.includes('mini-trips'):
return miniTrips;
case url.includes('sailing-trips'):
return sailingTrips;
default:
return epicTrips;
}
};
This is what I have so far, I am just not sure how or even if this is possible to pass in a function and loop over the url to determine what string is present in the url and then deliver that value to the user.
function getTravelInfo2 () {
const url = window.location.href;
var travel = {
'trips': epicTrips,
'mini-trips': miniTrips,
'sailing-trips': sailingTrips,
'default': epicTrips
};
return travel[url];
}
My solution was to First get what I need from the URL Via
const url = 'http://www.test.com/trips';
firstPart = url.split('/', -1)[-1];
getTravelInfo(firstPart);
const getTravelInfo = (type) => {
var travel = {
'trips': epicTrips,
'mini-trips': miniTrips,
'sailing-trips': sailingTrips,
'default': epicTrips
};
return (travel[type] || travel['default']);
}
Much cleaner and easier to execute and understand.

Unable to share aliases across steps in cypress

I am having an issue trying to share an alias between two steps in cypress. Basically what I am trying to do is in the first then step, I get the value and pass it in as a variable storedOddsValue. In the second then step, when I try to get that alias value and complete the step, it fails because I beleive it's coming back with undefined.
I know this because instead of using:
let storedOddsValue = oddslib.from("fractional", cy.get("#storedOddsValue"));
If I replace the alias with the hardcoded value like so:
let storedOddsValue = oddslib.from("fractional", "13/5");
The whole code below executes fine and the assertion passes. So my question is what am I doing wrong, why is it not getting the alias?
Then ("The prices are formatted in fractions by default", () => {
priceFormatElements.priceFormatDropdown().should('have.value', 'Fraction');
const oddsValue = oddsSelectionElements.oddsButton().first().invoke("text");
oddsValue.then((oddsValue) => {
expect(oddsValue.trim()).contains("/");
oddsValue.trim();
}).as("storedOddsValue")
})
Then ("The prices are formatted in {string}", (priceFormat) => {
let expectedOddsValue;
let currentOddsValue = oddsSelectionElements.oddsButton().first().invoke("text");
//let storedOddsValue = oddslib.from("fractional", "13/5");
let storedOddsValue = oddslib.from("fractional", cy.get("#storedOddsValue"));
switch (priceFormat) {
case "Fractional":
expectedOddsValue = storedOddsValue.to("fractional");
break;
case "Decimal":
expectedOddsValue = storedOddsValue.to("decimal", {precision: 2});
break;
case "American":
expectedOddsValue = "+" + storedOddsValue.to("moneyline");
break;
default:
throw new Error('unknown price format: ' + priceFormat);
}
currentOddsValue.then((oddsValue) => {
expect(oddsValue.trim()).equals(expectedOddsValue);
});
})
Not sure what oddslib.from does, but to use it with an alias you need to nest in this way
Then (... () => {
let expectedOddsValue
let currentOddsValue = oddsSelectionElements.oddsButton().first().invoke("text")
cy.get("#storedOddsValue").then(aliasValue => {
let storedOddsValue = oddslib.from("fractional", aliasValue)
switch (priceFormat) {
...
}
currentOddsValue.then((oddsValue) => {
expect(oddsValue.trim()).equals(expectedOddsValue);
});
}) // end of cy.get("#storedOddsValue")
})
Because cy.get("#storedOddsValue") is asynchronous, and the switch() runs before it gives it's value.

Refactor this function to reduce its Cognitive Complexity from 18 to the 15 allowed

On Sonarqube I'm getting this as critical issue, can somebody help me in this to sort out this issue.
Here is the code details, please let me know how can I refactor this code with switch case :
const getEmailTo = (subject) => {
switch (subject) {
case 'POWERUP_REWARDS':
return COMMON_EMAIL;
case 'GAME_INFORMER':
return COMMON_EMAIL;
case 'ONLINE_ORDER':
return 'test#gmail.com';
case 'STORE_EXPERIENCE':
return 'test#gmail.com';
case 'APP_REVIEW':
return COMMON_EMAIL;
case 'SOMETHING_ELSE':
return COMMON_EMAIL;
default:
return '';
}
};
Make your code a bit more DRY and simplified by collecting your common cases that return the same values. I don't think SonarQube validates readability, but proper use of whitespace can go a long way in readability of unfamiliar code. It's opinionated, but I like a line between case returns.
const getEmailTo = (subject) => {
switch (subject) {
case 'POWERUP_REWARDS':
case 'GAME_INFORMER':
case 'APP_REVIEW':
case 'SOMETHING_ELSE':
return COMMON_EMAIL;
case 'ONLINE_ORDER':
case 'STORE_EXPERIENCE':
return 'test#gmail.com';
default:
return '';
}
};
You can use an object to map subjects to email addresses and return the email address corresponding to the subject.
const TEST_EMAIL = 'test#gmail.com'
const emails = {
POWERUP_REWARDS: COMMON_EMAIL,
GAME_INFORMER: COMMON_EMAIL,
APP_REVIEW: COMMON_EMAIL,
SOMETHING_ELSE: COMMON_EMAIL,
ONLINE_ORDER: TEST_EMAIL,
STORE_EXPERIENCE: TEST_EMAIL,
}
const getEmailTo = (subject) => emails[subject] || ''
I'm not sure if returning '' is fine when a subject is not in the mapping. I would suggest throwing an error to let the caller know the subject is invalid.
const getEmailTo = (subject) => {
if (!(subject in emails)) {
throw new Error(`Invalid subject: ${subject}`)
}
return emails[subject]
}
You can just put the related case statements next to each other, like this:
const getEmailTo = (subject) => {
switch (subject) {
case 'POWERUP_REWARDS':
case 'GAME_INFORMER':
case 'APP_REVIEW':
case 'SOMETHING_ELSE':
return COMMON_EMAIL;
case 'ONLINE_ORDER':
case 'STORE_EXPERIENCE':
return 'test#gmail.com';
default:
return '';
}
};

RegEx that match react router path declaration

I have a map of routes with react router path as keys, eg:
const routes = [
{
page: "mySettings",
label: "pages.mySettings",
path: "/professionels/mes-reglages.html",
exact: true
},
{
page: "viewUser",
label: "pages.viewUser",
path: "/users/:id/view.html",
exact: true
}
];
I want from a location retrieved with useHistory().location.pathname, to match all the path that match the key in react-router terms, eg:
(pathname) => get(routesMap, "/professionels/mes-reglages.html") => should match routesMap.get('/professionels/mes-reglages.html')
(pathname) => get(routesMap, "/users/11/view.html") => should match routesMap.get('/users/:id/view.html')
and all react-router paths so this should work too:
(pathname) => get(routesMap, "/users/11/settings/10/items/24/view.html") => should match routesMap.get('/users/:userId/settings/:settingId/items/:id/view.html')
I have started here, any idea how I can do that with a regexp?
https://codesandbox.io/s/youthful-wing-fjgm1
Based on your comments i adjusted the code a bit and wrote a rapper function for your lookup.
The following rules you have to watch out for when creating the urls:
The last id always gets replaced by {id}
All other ids get replaced by url part to id without plural and "Id" attached ("/users/111" -> "/users/{userId}")
This would be the function:
const getRouteFromPath = (map, url) => {
if (url.match(/\/\d+\//g).length > 1) {
let allowedUrlPart = getAllowedIdQualifier(map);
let urlParts = url.match(/(?<=\/)\w+(?=\/\d+\/)/g);
urlParts.forEach(val => {
if (!allowedUrlPart.includes(val)) {
urlParts = urlParts.slice(urlParts.indexOf(val), 1);
}
});
urlParts.forEach((val, key, arr) => {
if (key === arr.length - 1) {
let regex = new RegExp("(?<=/" + val + "/)\\d+", "g");
let replacement = ":id";
url = url.replace(regex, replacement);
} else {
let regex = new RegExp("(?<=/" + val + "/)\\d+", "g");
let replacement = ":" + val.slice(0, -1) + "Id";
url = url.replace(regex, replacement);
}
});
return map.get(url);
} else {
url = url.replace(/\/\d+\//g, "/:id/");
return map.get(url);
}
};
const getAllowedIdQualifier = map => {
let allowedQualifiers = [];
map.forEach(val => {
let allowed = val.path.match(/(?<=\/)\w+(?=\/:)/g);
allowed.forEach(e => {
if (!allowedQualifiers.includes(e)) {
allowedQualifiers.push(e);
}
});
});
return allowedQualifiers;
};
export default getRouteFromPath;
As parameter you pass in the url to match as first parameter and the map of routes as the second paramter and call the function getRoute() instead of the direct map.get() call you where using before.
Here is the example with the urls adjusted to follow the rules, since you need some rules to be able to apply RegEx.
EDIT:
I adjusted the script, so that it reads the map first and determines the allowed paths which accept a id and then check the possible ids from an actual url against it.
https://codesandbox.io/s/kind-moon-9oyj9?fontsize=14&hidenavigation=1&theme=dark

Instantiating a new function no access to inner function

This is quite a frustrating issue I am stuck on. I am exporting a parent function with quite a rigid structure for the purpose of library mocking.
const responses = require("../responses/index.js");
function fetch (
first = "contactsMultiple",
second = "passwordFail",
third = "contactsMultiple") {
this.first = first;
this.second = second;
this.third = third;
this.iteration = 1;
this.fetch = () => {
let body;
switch (this.iteration) {
case 1 :
body = responses.contacts[this.first];
break;
case 2 :
body = responses.password[this.second];
break;
case 3 :
body = responses.contacts[this.third];
break;
default:
body = responses.contacts["contactsMultiple"];
break;
}
const response = {
status: 200,
statusText: "OK",
json: () => {
return body;
}
};
this.iteration++;
return response;
}}
module.exports = fetch;
I export this, then import it, create a new instance of the function so I can set properties that I wish to increment and also set via the construction of the function. But the code then expects a function called fetch.
I need to be able to call it like:
const Fetch = new fetch();
then pass Fetch into the existing classes.
If I log this new function I can see it has a fetch property of a type function. but I keep getting this.fetch is not a function.
What am I missing?
Many thanks

Categories