I have to upgrade my app to display pages based on a users type and role properties. Currently I employ a simple switch statement to do this based on user type, e.g.
switch(type) {
case 'a':
return CONSTANT.ONE;
case 'b':
return CONSTANT.TWO;
default:
return null;
}
The switch just returns a constant string which dictates the view showm, but that isn't scalable as number of types , roles increases. Can anyone suggest a good pattern to use in this case. I thought a state pattern might be good but is that over the top just to return a string ?
Thanks
Very similarly to #MarkusJarderot, but with a few important differences in behavior, I would use:
var mapping = {
'a': CONSTANT.ONE,
'b': CONSTANT.TWO,
'_default': null
};
return mapping.hasOwnProperty(type) ? mapping[type] : mapping["_default"];
When the value of mapping[type] is falsy, this will still return it, rather than going to the null alternative. That will be very helpful when one of your values is 0 or an empty string.
Use an object as a lookup:
var roles = {};
Then you can add roles like this:
roles['a']=CONSTANT.ONE;
and look them up like this:
var xxx = roles['a'];
This way you can add things to the roles in different places in your code
You can use Strategy Pattern:
//Example without strategy pattern
gameDifficulty(difficulty) {
switch(difficulty){
case 'easy':
easyGameMode();
break;
case 'difficult'
difficultMode();
break;
}
}
// Using Strategy
const strategies = {
easy: easyGameMode(),
difficult: difficultGameMode(),
//More strategies
__default__: normalGameMode()
}
const easyGameMode = (game) => {
game.difficulty(1);
//Do easy game mode stuff in here
return game;
}
const normalGameMode= (game) => {
game.difficulty(2);
//Do normal game mode stuff in here
return game;
}
const difficultGameMode = (game) => {
game.difficulty(3);
//Do difficult game mode stuff in here
return game;
}
const startGame = (game, difficulty) => {
const gameModifier = strategies[difficulty] ?? strategies.__default__;
return gameModifier(game, difficulty);
}
More info in this article.
Related
I'm creating a temperature conversion app in Angular 7, in my formGroup I have an input/output value, and 2 multi selects where the user can select a unit of temperature 'From' and 'To' to convert(celsius, Fahrenheit or Kelvin).
When the user submit the form in my component I get the input data
submitForm(data) {
this.input = data.input;
this.unitInput= data.unitInput;
this.unitTarget= data.unitTarget;
this.output= data.output;
}
I thought in just adding if statements to call the function that will make the conversion, but now looking at all if statements that i have seems a lot and not a good design.
if (this.inputUnit === 'Celsius' && this.targetUnit === 'Fahrenheit') {
this.celsiusToFahrenheitConversion ();
}
if (this.inputUnit === 'Celsius' && this.targetUnit === 'Kelvin') {
this.celsiusToKelvinConversion ();
}
if (this.inputUnit === 'Celsius' && this.targetUnit === 'Rankine') {
this.celsiusToRankineConversion ();
}
If's for Fahrenheit, If's for Kelvin, If's for Rankine, etc..
and what about if in the feature I want to add a different kind of unit?
What would be a better approach?
Thanks in advance
I think the cleanest way to handle this would be using a switch statement. It doesn't get simpler than that, I personally hate it as well but sometimes there is just no way around it really.
switch(this.inputType) {
case 'Celsius':
switch(this.targetUnit) {
case 'Fahrenheit':
this.celsiusToFahrenheitConversion();
break;
case 'Kelvin':
this.celsiusToKelvinConversion();
break;
case 'Rankine':
this.celsiusToRankineConversion();
break;
}
break;
case 'Fahrenheit':
break;
case 'Rankine':
break;
case 'Kelvin':
break;
}
However! This is a mathematical conversion. F° to C° (32°F − 32) × 5/9, F° to Kelvin is (32°F − 32) × 5/9 + 273.15, F° to Rankine is 32°F + 459.67 and so on. And where is math, there is a system. My approach would be creating a conversion table - an object, and use the form values as lookup values. For example:
const magicalLookupTable = {
celsius: {
fahrenheit: () => {
/** Implementation */
},
kelvin: () => {
/** Implementation */
}
},
fahrenheit: {
celsius: () => {
/** Implementation */
},
kelvin: () => {
/** Implementation */
}
}
}
When I look at this I see compound keys set against a function value.
One way to represent this idea is with an typical object where the keys are the compound and they map to a specific function:
const conversionFunctions = {
'celsius-fahrenheit': this.celsiusToFahrenheitConversion,
'celsius-kelvin': this.celsiusToKelvinConversion,
'celsius-rankine': this.celsiusToRankineConversion
};
const key = `${this.inputUnit.toLowerCase()}-${this.targetUnit.toLowerCase()}`;
const convert = conversionFunctions[key];
convert();
I'm looking for a clean way to get true or false based on an order of permissions based on the following rules:
Starts with Company Permissions as a default
Then to Team Permissions if permission defined
Finally to User Permissions if permission is
This would need to also handle undefined. So basically wanting to see if there's some "clean" way to do this without having to conditionally check each value and moving on.
In this example, the result should be false since there are no User Permissions defined and the Team Permissions has false.
const UserPermissions = {}
const TeamPermissions = {
PERMISSION_ONE: false
}
const CompanyPermissions = {
PERMISSION_ONE: true
}
const hasPermissions = UserPermissions.PERMISSION_ONE || TeamPermissions.PERMISSION_ONE || CompanyPermissions.PERMISSION_ONE
console.log(hasPermissions)
Thanks!
From my understanding, the rules are:
ignore undefined
return true of false, whatever comes first
This little function should handle that, not sure how you want to deal with empty (or all undefined) arguments.
let x;
let t = true;
let f = false;
let firstBool = (...a) => Boolean(a.filter(x => typeof x !== 'undefined').shift());
console.log(firstBool(x,t,f));
console.log(firstBool(t,x,f));
console.log(firstBool(x,f,t));
console.log(firstBool());
In your example, that would be
const hasPermissions = firstBool(
UserPermissions.PERMISSION_ONE,
TeamPermissions.PERMISSION_ONE,
CompanyPermissions.PERMISSION_ONE
]
If you are looking for the same property name in multiple objects, you might be able to slightly alter the technique in georg's answer:
const firstBoolProp = (...objs) => (prop) =>
objs.reduce((a, o) => Boolean(a) === a ? a : o[prop], undefined)
const UserPermissions = {}
const TeamPermissions = {PERMISSION_ONE: false}
const CompanyPermissions = {PERMISSION_ONE: true}
console .log (
firstBoolProp
(UserPermissions, TeamPermissions, CompanyPermissions)
('PERMISSION_ONE')
)
You can then use a single function to multiple permissions against that same set of permission objects:
const getPermissions = firstBoolProp(UserPermissions, TeamPermissions, CompanyPermissions)
const perms = {
p1: getPermissions('PERMISSION_ONE'),
p2: getPermissions('PERMISSION_TWO'),
}
//=> {p1: false, p2: undefined}
And if you want to use an array rather than individual parameters, you can simply replace (...obj) => with (obj) =>
One way to do this is to store the permissions in an array and reduce it to a boolean:
/**
*
* #param {{PERMISSION_ONE:boolean}[]} permissions
*/
function anyoneHasPermission(permissions, name = "PERMISSION_ONE") {
for (const perm of permissions) {
if (perm[name]) {
return true;
}
}
}
console.log(anyoneHasPermission([UserPermissions, TeamPermissions, CompanyPermissions]))
You need to be more specific in what you want to accomplish. Choosing the right data structure is usually more important than choosing an algorithm. Indeed, sometimes the right data structure makes the algorithm disappear completely.
Throw the permissions into an array in the order that you want them validated.
Iterate the array and if any of your conditions is not met then return false and break.
This will stop you running down the entire chain if say your top level permission is not set (because nothing beyond that matters anymore and no use validating it)
const UserPermissions = {PERMISSION_ONE: true}
const TeamPermissions = {}
const CompanyPermissions = {PERMISSION_ONE: true}
const permCheck = HasPerms([CompanyPermissions, TeamPermissions, UserPermissions]);
alert(permCheck);
function HasPerms(permArray){
for (var i = 0; i < permArray.length; i++) {
if (!permArray[i] || !permArray[i].PERMISSION_ONE) {return false;}
}
return true;
}
You can expand the function to dynamically take in the permission you would like to check but example shown hardcoded for simplicity sake.
In JS or OOP language the polymorhpism is created by making different types.
For example:
class Field {...}
class DropdownField extends Field {
getValue() {
//implementation ....
}
}
Imagine I have library forms.js with some methods:
class Forms {
getFieldsValues() {
let values = [];
for (let f of this.fields) {
values.push(f.getValue());
}
return values;
}
}
This gets all field values. Notice the library doesnt care what field it is.
This way developer A created the library and developer B can make new fields: AutocompleterField.
He can add methods in AutocompleterField withouth changing the library code (Forms.js) .
If I use functional programming method in JS, how can I achieve this?
If I dont have methods in object i can use case statements but this violates the principle. Similar to this:
if (field.type == 'DropdownField')...
else if (field.type == 'Autocompleter')..
If developer B add new type he should change the library code.
So is there any good way to solve the issue in javascript without using object oriented programming.
I know Js isnt exactly OOP nor FP but anyway.
Thanks
JavaScript being a multi-purpose language, you can of course solve it in different ways. When switching to functional programming, the answer is really simple: Use functions! The problem with your example is this: It is so stripped down, you can do exactly the same it does with just 3 lines:
// getValue :: DOMNode -> String
const getValue = field => field.value;
// readForm :: Array DOMNode -> Array String
const readForm = formFields => formFields.map(getValue);
readForm(Array.from(document.querySelectorAll('input, textarea, select')));
// -> ['Value1', 'Value2', ... 'ValueN']
The critical thing is: How is Field::getValue() implemented, what does it return? Or more precisely: How does DropdownField::getValue() differ from AutocompleteField::getValue() and for example NumberField::getValue()? Do all of them just return the value? Do they return a pair of name and value? Do they even need to be different?
The question is therefor, do your Field classes and their inheriting classes differ because of the way their getValue() methods work or do they rather differ because of other functionality they have? For example, the "autocomplete" functionality of a textfield isn't (or shouldn't be) tied to the way the value is taken from it.
In case you really need to read the values differently, you can implement a function which takes a map/dictionary/object/POJO of {fieldtype: readerFunction} pairs:
/* Library code */
// getTextInputValue :: DOMNode -> String
const getTextInputValue = field => field.value;
// getDropdownValue :: DOMNode -> String
const getDropdownValue = field => field.options[field.selectedIndex].value;
// getTextareaValue :: DOMNode -> String
const getTextareaValue = field => field.textContent;
// readFieldsBy :: {String :: (a -> String)} -> DOMNode -> Array String
readFieldsBy = kv => form => Object.keys(kv).reduce((acc, k) => {
return acc.concat(Array.from(form.querySelectorAll(k)).map(kv[k]));
}, []);
/* Code the library consumer writes */
const readMyForm = readFieldsBy({
'input[type="text"]': getTextInputValue,
'select': getDropdownValue,
'textarea': getTextareaValue
});
readMyForm(document.querySelector('#myform'));
// -> ['Value1', 'Value2', ... 'ValueN']
Note: I intentionally didn't mention things like the IO monad here, because it would make stuff more complicated, but you might want to look it up.
In JS or OOP language the polymorhpism is created by making different types.
Yes. Or rather, by implementing the same type interface in different objects.
How can I use Javascript polymorphism without OOP classes
You seem to confuse classes with types here. You don't need JS class syntax to create objects at all.
You can just have
const autocompleteField = {
getValue() {
…
}
};
const dropdownField = {
getValue() {
…
}
};
and use the two in your Forms instance.
Depends on what you mean by "polymorphism". There's the so-called ad-hoc polymorphism which type classes in Haskell, Scala, or PureScript provide -- and this kind of dispatch is usually implemented by passing witness objects along as additional function arguments, which then will know how to perform the polymorphic functionality.
For example, the following PureScript code (from the docs), which provides a show function for some types:
class Show a where
show :: a -> String
instance showString :: Show String where
show s = s
instance showBoolean :: Show Boolean where
show true = "true"
show false = "false"
instance showArray :: (Show a) => Show (Array a) where
show xs = "[" <> joinWith ", " (map show xs) <> "]"
example = show [true, false]
It gets compiled to the following JS (which I shortened):
var Show = function (show) {
this.show = show;
};
var show = function (dict) {
return dict.show;
};
var showString = new Show(function (s) {
return s;
});
var showBoolean = new Show(function (v) {
if (v) {
return "true";
};
if (!v) {
return "false";
};
throw new Error("Failed pattern match at Main line 12, column 1 - line 12, column 37: " + [ v.constructor.name ]);
});
var showArray = function (dictShow) {
return new Show(function (xs) {
return "[" + (Data_String.joinWith(", ")(Data_Functor.map(Data_Functor.functorArray)(show(dictShow))(xs)) + "]");
});
};
var example = show(showArray(showBoolean))([ true, false ]);
There's absolutely no magic here, just some additional arguments. And at the "top", where you actually know concrete types, you have to pass in the matching concrete witness objects.
In your case, you would pass around something like a HasValue witness for different forms.
You could use a the factory pattern to ensure you follow the open close principle.
This principle says "Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".
class FieldValueProviderFactory {
getFieldValue(field) {
return this.providers.find(p => p.type === field.type).provider(field);
}
registerProvider(type, provider) {
if(!this.providers) {
this.providers = [];
}
this.providers.push({type:type, provider:provider});
}
}
var provider = new FieldValueProviderFactory();
provider.registerProvider('DropdownField', (field) => [ 1, 2, 3 ]);
provider.registerProvider('Autocompleter', (field) => [ 3, 2, 1 ]);
class FieldCollection {
getFieldsValues() {
this.fields = [ { type:'DropdownField',value:'1' }, { type:'Autocompleter',value:'2' } ];
let values = [];
for (let field of this.fields) {
values.push(provider.getFieldValue(field));
}
return values;
}
}
Now when you want to register new field types you can register a provider for them in the factory and don't have to modify your field code.
new Field().getFieldsValues();
In Javascript, is there a way to achieve something similar to this ?
const databaseObjectID = "someId"; // like "product/217637"
switch(databaseObjectID) {
case includes('product'): actionOnProduct(databaseObjectID); break;
case includes('user'): actionOnUser(databaseObjectID); break;
// .. a long list of different object types
}
This is more a curiosity question to understand the possibilities of switch / case, as in this particular case I have solved my problem using const type = databaseObjectID.split('/')[0]; and apply the switch case on type
This will work, but it shouldn't be used in practice.
const databaseObjectID = "someId"; // like "product/217637"
switch(true) {
case databaseObjectID.includes('product'): actionOnProduct(databaseObjectID); break;
case databaseObjectID.includes('user'): actionOnUser(databaseObjectID); break;
// .. a long list of different object types
}
You usage would be considered an abuse of case.
Instead just use ifs
if (databaseObjectId.includes('product')) actionOnProduct(databaseObjectID);
else if (databaseObjectId.includes('user')) actionOnUser(databaseObjectID);
// .. a long list of different object types
If the ObjectId contains static content around the product or user, you can remove it and use the user or product as a key:
var actions = {
"product":actionOnProduct,
"user" :actionOnUser
}
actions[databaseObjectId.replace(/..../,"")](databaseObjectId);
Sorry, I'm a noob so someone will probably have to clean this up, but here is the idea. Pass to a function to check and return a category then use the switch.
function classify(string){
var category = categorize(string);
switch (category) {
case 'product':
console.log('this is a product');
break;
case 'user':
console.log('this is a user');
break;
default:
console.log('category undefined');
}
}
function categorize(string){
if (string.includes('product')){
return 'product';
}
if (string.includes('user')){
return 'user';
}
}
classify("product789");
classify("user123");
classify("test567");
Sorry, as well, for not matching your example.
Question:
use string “includes()” in switch Javascript case
While the includes() method will work, it is case sensitive, and just matches any characters. I have found a Regex solution that I like much better, and provides a lot of flexibility. For example, you could easily change this to match only WORDS.
var sourceStr = 'Some Text and literaltextforcase2 and more text'
switch (true) { // sourceStr
case (/LiteralTextForCase1/i.test(sourceStr)):
console.log('Case 1');
break;
case (/LiteralTextForCase2/i.test(sourceStr)):
console.log('Case 2');
break;
default:
console.log('ERROR No Case provided for: ' + sourceStr);
};
//-->Case 2
I do not want to use Switch in my code, so I'm looking for some alternative
Example with Switch:
function write(what) {
switch(what) {
case 'Blue':
alert ('Blue');
break;
...
case 'Red':
alert ('Red');
break;
}
}
Example without Switch:
colors = [];
colors['Blue'] = function() { alert('Blue'); };
colors['Red'] = function() { alert('Red'); };
function write(what) {
colors[what]();
}
My questions are:
Do you know any other alternatives?
Is this best solution?
I have only a note about your second approach, you shouldn't use an Array to store non-numeric indexes (that you would call in other languages an associative array).
You should use a simple Object.
Also, you might want to check if the what argument passed to your write function exists as a property of your colors object and see if it's a function, so you can invoke it without having run-time errors:
var colors = {};
colors['Blue'] = function() { alert('Blue'); };
colors['Red'] = function() { alert('Red'); };
function write(what) {
if (typeof colors[what] == 'function') {
colors[what]();
return;
}
// not a function, default case
// ...
}
I used a structure like this today:
var chosenColor = 'red';
var colorString = {
'red': 'The color is red.',
'green': 'The color is green.',
'blue': 'The color is blue.',
}[chosenColor] || 'The color is unknown.';
I like that it's a really small amount of code to choose a string based on choice.
You could also pass it to a function:
alert({
'red': 'The color is red.',
'green': 'The color is green.',
'blue': 'The color is blue.',
}[chosenColor] || 'The color is unknown.');
You could use object literals, and try catch to trap the default:
function write(what) {
var colors = {
'Blue': function(){ alert('Light-Blue'); },
'Red': function(){ alert('Deep-Red'); },
'Green': function(){ alert('Deep-Green'); }
}
try {colors[what]();}
catch(err) {colors['Green']();}//default behaviour
}
write('Pink');
Question 2:
Generally, if you can replace custom control structures with a dictionary lookup, you're perfectly fine. It's easy to read and highly elegant -- stick with it.
I had to do do a compare for a group sort of object props for a list and did not want to do a switch/case for all the possibilities so I did an array of objects assignment to a numeric rank first so the case became a simple compare. This is only 4 possibilities but you get the drift of how to extend this to situation where a switch/case becomes unmanageable:
function mySort2(item1,item2){
var matrix = {
'repair': 4,
'r/r': 3,
'part': 2,
'misc': 1
};
(matrix[item1.category] < matrix[item2.category]) ? return +1 : return -1;
// if possible bad data need to check for this first ???
i1=matrix[item1.category] || null;
i2=matrix[item2.category] || null;
if (i1==null){
// handle bad data in item 1
return +1; // put it after 2
}
if (i2==null){
// ditto
return -1; //put 1 first
}
if (i1<i2)
return +1;
else
return -1;
}
You are pretty much there already. If possible you might want to add a helper function to make the setup easier. For Example:
function setup(what)
{
colors[what] = function() { alert(what); };
}
EDIT:
If what you want to do for each option is more complicated clearly this will not work. As mentioned in the comments by #roe this uses the global colors which is often frowned upon.
Alternatively, you can also use Dictionaries, so you could see the type of the function return, I think it's clean and scalable, although it's just pure JS.
const ColorDictionary = {
red: 'applies red color',
blue: ' applies blue color',
green: 'applies green color',
}
const useShowColors = (color) => {
// color will be selected or fallout to default value.
const getColor = () => (
ColorDicionary[color] ?? 'applies default color'
)
return { getColor }
}
const { getColor } = useShowColors() //pass the color you wish.
An alternative is to define a class with a write method, and override that in subclasses Red and Blue to do the right thing.
Whether or not that is better than your proposed solution, depends on your particular situation.
As I said, it's great. The only thing I can add to your solution is that it's perhaps better to localize your colors.
function write(what) {
var colors = [];
colors['Blue'] = function() { alert('Blue'); };
colors['Red'] = function() { alert('Red'); };
colors[what]();
}