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]();
}
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 have a list which shows a query of words from a db, from there i can click on one word and it gets pushed to another list which i can save than. With this i can create different wordlists. What i want to do is to give the words another color if i have already pushed them on my new list.
To do so i use a function in my controller to compare the two lists with and angular.foreach. If wordFromQuery._id === wordOnNewList._id i gave the words another background color with ng-style.
Here is my code:
View
ng-repeat="word in searchWords" ng-click="addWordToSet(word)" ng-class="isInside(word)" ng-style="{ background: choosenWords.value == 'exist' ? 'lightgreen' : 'white' }"
I iterate over the words query (searchWords) and with addWordtoSet(word) i push them to my other array (this works great). isInside(word) will do the angular.foreach to compare the two arrays and the ng-style should provide different styles, according to the if-statement from the isInside function.
Controller
$scope.isInside = function (word) {
angular.forEach($scope.currentWordlist, function (item) {
if (item._id === word._id) {
$scope.choosenWords = {value: 'exist'};
} else {
$scope.choosenWords = {value: 'notOnList'};
}
});
};
The angular.forEach compares the words from both arrays. currentWordList is the array in which i push with addWordToSet
What happens is that one word on the searchword array gets the green color (and its set of by +1, so if the word in arraypos. 0 would be right the arraypos. 1 gets the green color).
I suspect that i did it all wrong with the ng-class element, but i didnt found another good opportunity to get the word._id another way. Did i do something obviously wrong here?
I would appreciate tips or hints. Thanks!
UPDATE
It works quite fine with the addWordToSet function:
$scope.addWordToSet = function (word) {
var exists = false;
angular.forEach($scope.currentWordlist, function (item) {
if (item._id === word._id) {
exists = true;
}
});
if (exists === false) {
$scope.currentWordlist.push(word);
}
};
The only thing i need i think is not doing this on click but instantly without clicking anything. is my ng-class="isInside(word)" the right choice for that?
You can assign a color to a variable inside the same function and use it in the view.
$scope.isInside = function (word) {
angular.forEach($scope.currentWordlist, function (item) {
if (item._id === word._id) {
$scope.choosenWords = {value: 'exist'};
$scope.color = 'lightgreen'
} else {
$scope.choosenWords = {value: 'notOnList'};
$scope.color = 'white'
}
});
};
ng-style="{'background-color':color}"
View:
ng-repeat="word in searchWords" ng-click="addWordToSet(word)" ng-class="isInside(word)" ng-style="{'background-color':color}" }"
Try
$scope.choosenWords.value = 'exist';
Also initialize choosenWords at the start of the controller.
If this doesn't work check the order of priority of execution of the ng modules.
Is the controller initialized through a partial?
I sat together with a friend and we came up with a working version of this problem, so here is the solution in case someone has a similar problem and hand.
In the Controller we used the following function:
$scope.isSelected = function (word) {
var found = false;
angular.forEach($scope.currentWordlist, function (item) {
if (item._id === word._id) {
found = true;
}
});
return found;
};
It uses the foreach to compare the arrays and if there are ids that are a match the found bool returns true.
In the View we used the following:
ng-class="isSelected(word) ? 'marked' : 'unmarked'"
which uses the marked or unmarked css class for, in my case, coloring the matched words in green (marked). All other words are getting the background color white.
here is the CSS:
.marked {
background: $lightgreen;
}
.unmarked {
background: $nicewhite;
}
In my case i use scss and colorvariables, but you can, of course use all other colors like red; or #fff. The result of this are two arrays that are views. The first one is a searchquery from a DB which shows all words. The second is a new array in which you can push words by clicking on one of the words. If you do so the word gets pushed AND it gets a green background. Thats it, i hope this is good information.
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.
How can I create a value in java script map to behave like integer counter.
var map = { "cars" : count, "buses" : count };
The way I want to use this map is:
if (car) {
// incremeent the count of cars
} else {
// increment the count of busses
}
My question is how should such a map look like syntactically in javascript ?
You can initialize it like this:
var map = {cars: 0, buses: 0 };
Then, you can increment like this:
if (car) {
++map.cars;
} else {
++map.buses;
}
But, I suspect you need a little more logic to your if (car) so you can actually tell that it is a car type, not just a boolean but you haven't shown us how that part of your code works to know exactly what to suggest.
You could also be a little more object oriented and put the method on the map:
var map = {
cars: 0,
buses: 0,
count: function(obj) {
if (obj) {
++this.cars;
} else {
++this.buses;
}
}
};
map.count(item);
Again, you'll have to modify the if (obj) portion of this to actually discern whether the type is a car or not, but you haven't disclosed enough about your code to know how to do that.
var counts = { cars: 0, buses: 0 };
if (car) {
counts.cars++;
} else {
counts.buses++;
}
// or more succinctly:
counts[car ? 'cars' : 'buses']++;
If you have a parameter, say vehicles, whose value is either cars or buses, then you can do:
map[vehicles]++;
You can also do:
map[car? 'cars' : 'buses']++;
If you show more of what you are doing, more help can be provided on what is likely to be most appropriate.
Question
More out of curiosity, but I was wondering how to refactor an if statement to something cleaner / less brittle. From what I have read, polymorphism could have a use?
In the example I only want to return the first car if color:'red' is true.
Coffeescript
example: () ->
cars = [{color:'red', reg:'111'},{color:'blue', reg:'666'}]
if cars[0].color is 'red'
then cars[0]
else cars[1]
Javascript
example: function() {
var cars = [{color:'red',reg:'111'},{color:'blue',reg:'666'}];
if (cars[0].color === 'red') {
return cars[0];
} else {
return cars[1];
}
}
I understand this question maybe closed or moved due to the ambiguous nature
? : operator is exactly that, a "cleaner" if-else
http://msdn.microsoft.com/en-us/library/ty67wk28.aspx
classify = (input < 0) ? "negative" : "positive";
There are also switch statements for larger combinations:
http://www.w3schools.com/js/js_switch.asp
switch(n)
{
case 1:
execute code block 1
break;
case 2:
execute code block 2
break;
default:
code to be executed if n is different from case 1 and 2
}
Polymorphism is an abstract concept, not a way to write a statement. It's the practice of creating a method/function/class/etc where type is at least SOMEWHAT ambiguous. So the same method could return a result if fed, say, an integer for parameter 1, the same as if you were to feed an array into the same parameter.
You can use ternary operator, its syntax is condition ? result1 : result2;
return cars[0].color === 'red' ? colors[0] : colors[1]
Just for fun :
// red -> +false -> 0
// not red -> +true -> 1
return cars[+(cars[0].color !== 'red')];
Turning Car into an object:
function Car(options) {
this.options = {};
// Some default options for your object
$.extend(this.options, {
color: "green",
buildYear: 1990,
tires: 4,
brand: "merceded"
}, options);
}
// A method registered on the prototype
Car.prototype.getColor = function () {
return this.options.color;
};
var myToyota = new Car({
brand: "toyota"
});
console.log("My Toyota is: "+ myToyota.getColor());
example: http://jsfiddle.net/YthH8/
Keep in mind that are are many ways you can use objects / inheritance in JavaScript.
Coffee script has it's own syntactic sugar for using classes => http://coffeescript.org/#classes
There is a ternar operator ? used mostly when you don't want to use if-else statement:
example: function() {
var cars = [{color:'red',reg:'111'},{color:'blue',reg:'666'}];
return cars[0].color === 'red' ? cars[0] : cars[1];
}
const example = () => {
var cars = [{color:'red',reg:'111'},{color:'blue',reg:'666'}];
return (cars[0].color === 'red' && cars[0]) ||
cars[1];
}