Is there an alternative to using IF / ELSE statements - javascript

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];
}

Related

Set of Tuples in JavaScript?

What is the best way to implement a Set of coordinates in JavaScript? I would like to be able to do things like:
let s=new Set();
s.add([1,1]);
if (s.has([1,1])) // false, since these are different arrays
The above doesn't work, since the Set is storing a reference to the array instead of the contents.
You can subclass Set for more flexibility.
class ObjectSet extends Set{
add(elem){
return super.add(typeof elem === 'object' ? JSON.stringify(elem) : elem);
}
has(elem){
return super.has(typeof elem === 'object' ? JSON.stringify(elem) : elem);
}
}
let s=new ObjectSet();
s.add([1,1]);
console.log(s.has([1,1]))
console.log(s.has([1,2,3]));
console.log([...s]);
console.log([...s].map(JSON.parse));//get objects back
This can be done with strings:
let s=new Set();
s.add("1,1");
s.add("2,2");
console.log(s.has("1,1"), s.has("1,2")); // true false
However, I would prefer to do this with some type of numeric tuple to avoid repeated string conversion logic.
If you only plan to store pairs of coords, another possibility is to use a combination of a Map (for the first coord) and a Set (for the second coord).
function TupleSet() {
this.data = new Map();
this.add = function([first, second]) {
if (!this.data.has(first)) {
this.data.set(first, new Set());
}
this.data.get(first).add(second);
return this;
};
this.has = function([first, second]) {
return (
this.data.has(first) &&
this.data.get(first).has(second)
);
};
this.delete = function([first, second]) {
if (!this.data.has(first) ||
!this.data.get(first).has(second)
) return false;
this.data.get(first).delete(second);
if (this.data.get(first).size === 0) {
this.data.delete(first);
}
return true;
};
}
let mySet = new TupleSet();
mySet.add([0,2]);
mySet.add([1,2]);
mySet.add([0,3]);
console.log(mySet.has([1,3]));
console.log(mySet.has([0,2]));
mySet.delete([0,2]);
console.log(mySet.has([0,2]));
Unfortunately, unlike a normal Set for which:
You can iterate through the elements of a set in insertion order.
— MDN Set
This approach will, for the example above, iterate in the order:
[0,2]
[0,3]
[1,2]
I was building a game when I came across this problem.
Here's a typescript class that might be able to help you. It uses a tree to do its magic.
You should be able to easily modify this to use arrays instead of the x and y parameters
// uses an internal tree structure to simulate set-like behaviour
export default class CoordinateSet {
tree: Record<number, Record<number, boolean>> = {}
add(x: number, y: number) {
this.tree[x] ||= {}
this.tree[x][y] = true;
}
remove(x: number, y: number) {
// if the coordinate doesn't exist, don't do anything
if (!this.tree[x] || !this.tree[y]) {
return;
}
// otherwise, delete it
delete this.tree[x][y];
// if the branch has no leaves, delete the branch, too
if (!Object.keys(this.tree[x]).length) {
delete this.tree[x]
}
}
has(x: number, y: number) {
return !!this.tree[x]?.[y];
}
}
And tests, which will also show you how it works:
import CoordinateSet from "./CoordinateSet";
describe("CoordinateSet", () => {
it("Can add a coordinate", () => {
const cs = new CoordinateSet();
expect(cs.has(1,1)).toBeFalsy();
cs.add(1, 1);
expect(cs.has(1,1)).toBeTruthy();
});
it("Can remove a coordinate", () => {
const cs = new CoordinateSet();
cs.add(1, 1);
expect(cs.has(1,1)).toBeTruthy();
cs.remove(1,1);
expect(cs.has(1,1)).toBeFalsy();
})
})
If we can assume that our tuples are finite integers, they could be encoded as a float.
class TupleSet extends Set{
add(elem){
return super.add((typeof elem === 'object'
&& Number.isSafeInteger(elem[0])
&& Number.isSafeInteger(elem[1]))
? elem[0]+elem[1]/10000000 : elem);
}
has(elem){
return super.has((typeof elem === 'object'
&& Number.isSafeInteger(elem[0])
&& Number.isSafeInteger(elem[1]))
? elem[0]+elem[1]/10000000 : elem);
}
}
function TupleSetParse(elem){
return (Number.isFinite(elem)?
[Math.round(elem),Math.round((elem-Math.round(elem))*10000000)]:elem);
}
let s=new TupleSet();
s.add([1,5]);
s.add([1000000,1000000]);
s.add([-1000000,-1000000]);
console.log(s.has([1,5])); // true
console.log(s.has([1,2])); // false
console.log([...s].map(TupleSetParse));
// [ [ 1, 5 ], [ 1000000, 1000000 ], [ -1000000, -1000000 ] ]
Of course, this is limited in range. And it is fragile to some malformed input, so additional error checking should be added. However, after some testing, this method is only 25% better in speed and memory usage than the JSON.stringify approach. So, JSON is the preferred approach.

Using conditionals inside template literals

I know there are more elegant ways to define a string with variables included,
but if I want to add a conditional in pre ES6 I would do..
var a = "text"+(conditional?a:b)+" more text"
now with template literals I would do..
let a;
if(conditional) a = `test${a} more text`;
else a = `test${b} more text`;
Is there a more elegant way to implement this conditional? is it possible to include if shortcut?
Use this:
let a = `test${conditional ? a : b} more text`;
You can also expand this a bit further and use placeholders inside such a conditional.
It really depends on the use case you have which is the most readable.
Some examples:
// example 1
const title = 'title 1';
const html1 = `${title ? `<h2>${title}</h2>` : '<h2>nothing 1</h2>'}`
document.getElementById('title-container-1').innerHTML = html1;
// example 2
const title2= 'title 2';
const html2 = `
${title2 ?
`<h2>${title2}</h2>` :
"<h2>nothing 2</h2>"
}`
document.getElementById('title-container-2').innerHTML = html2;
// example 3
const object = {
title: 'title 3'
};
const html3 = `
${(title => {
if (title) {
return `<h2>${title}</h2>`;
}
return '<h2>Nothing 3</h2>';
})(object.title)
}
`;
document.getElementById('title-container-3').innerHTML = html3;
<div id="title-container-1"></div>
<div id="title-container-2"></div>
<div id="title-container-3"></div>
(source)
If you have a simple condition to check, use the ternary operator, as it as been said, but if you have more complex or nested conditions, just call a function in this way:
const canDrink = (age, hasToDrive) => {
if (age >= 18 && !hasToDrive ) {
return 'Yeah, no problem'
} else if ( age >= 18 && hasToDrive ){
return 'Maybe not that much'
} else {
return 'Not at all'
}
}
console.log(`Can you drink tonight? ${ canDrink(21, true) }`) // Can you drink tonight? Maybe not that much
let t1 = "hey";
let t2 = "there";
console.log(`${t1}${t2 ? `(${t2})` : ''}`);
You can even do this:
${a || b}
Means: If a is null, use b.

Generating variable Leaflet marker colors bases on arrays

I'm trying to give the points on my map a dynamic color, based on an array filled with colors.
Currently, the colors in my code are defined in a function:
function getColor(keten) {
return keten == "MacDonalds" ? '#800888' :
keten == "Subway" ? '#969696' :
keten == "KFC" ? '#081d58' :
'#252525' ;
}
Which is called in Leaflet:
window["mapDataLayer"] = L.geoJson(geojson, {
pointToLayer: function (feature, latlng) {
var markerStyle = {
fillColor: getColor(feature.properties.Fastfoodketen),
........
Because of reasons, I now have dynamic data, and need to change my code a bit.
I've created two arrays, one filled with colors and one (dynamically) filled with restaurant chain names. Then I loop through both arrays and try to generate a variable that looks like the data in my getColor() function. That variable could then be returned in the same way.
arrayKleur = ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99", "#e31a1c", "#fdbf6f", "#ff7f00", "#6a3d9a", "#b15928"];
// filled for testing purposes, normally it's filled somewhere else
arrayKeten = ["Kwalitaria", "NYPizza", "Dominos", "BurgerKing", "KFC", "Subway", "MacDonalds"];
var leafletData = [];
for (i= 0; i < arrayKeten.length; i++){
leafletData += 'keten == '+arrayKeten[i]+' ? '+arrayKleur[i]+' :</br>';
}
leafletData += 'red;' ;
function getColor(keten) {
return leafletData;
}
With this new code I get a string which look just like the content of getColor(), but I think Leaflet can't work with a string, because all the markers on my map are black now.
I tried changing my original getColor() function to incorporate my arrays, just to see if it works.
function getColor(keten) {
return keten == arrayKeten[0] ? arrayKleur[0]:
keten == arrayKeten[1] ? arrayKleur[1]:
keten == arrayKeten[2] ? arrayKleur[2]:
'#999999' ;
}
This works flawlessly, so I think I'm pretty close to the solution with my string.
What do I need to change to get my code to work properly? Or am I maybe overcomplicating things?
You're overcomplicating things. Your string is not a color, is a string of text that looks like javascript but is not evaluated. And using eval() is wrong 99.9999% of the time.
Instead, use good ol' indexOf:
function getColor(keten) {
var i = arrayKeten.indexOf(keten);
if (i !== -1) {
return arrayKleur[ i ];
} else {
return '#999999';
}
}
Why not use an object for the colors with a default value?
function getColor(keten) {
var colors = { Kwalitaria: "#a6cee3", NYPizza: "#1f78b4", Dominos: "#b2df8a", BurgerKing: "#33a02c", KFC: "#fb9a99", Subway: "#e31a1c", MacDonalds: "#fdbf6f" };
return colors[keten] || '#999999';
}

Replace switch with a better pattern (Javascript)

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.

Alternative to the "switch" Statement

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]();
}

Categories