Remove if statement - javascript

If if statement could be avoided it is considered a good practice.
For example this code:
if (a > 80) {
a = 80;
}
Can become this:
a = Math.min(80, a);
That way the code is considered cleaner because there is no branch logic.
But is there any way to avoid if for more complex problems like this:
if (array.length > 5) {
array = array.reverse().join('');
} else {
array = 'array is lte 5';
}
If array length is > 5 then reverse it and join it, otherwise return "array is lte 5".
This is simple example but more complex than the first example and it's hard to remove if.
How mathematics handle branches and is it possible to express this logic in mathematics.
I can extract it to a separate method but it will only move the if statement in the method itself, it will not remove it.
I can imagine I can use some functions from Ramdajs but i didn't find appropriate one and even if I find one the if will be there i guess - it will be only abstracted.
Also imagine this sudo code:
if (file_exists(file)) {
content = file_read(file);
if (content.startsWith('config')) {
ret = 'config:';
} else if (content.endsWith(':app')) {
ret = ':app';
}
} else {
ret = '';
}
This code has only 2 if statements but already is a nightmare to read and change.
Is it possible to use mathematic and/or express it more clearly avoiding branches.
I know in mathematics there is no "read file" but it was just an example.
Thank you

One approach would be to put the thing you need to operate on in a "box" on which you apply a series of operations (i.e. functions). This forces you to remove any nested conditions.
This pseudo-code:
if (file_exists(file)) {
content = file_read(file);
if (content.startsWith('config')) {
ret = 'config:';
} else if (content.endsWith(':app')) {
ret = ':app';
}
} else {
ret = '';
}
could be replaced with:
const ret =
[file]
.map(x => file_exists(x) ? file_read(x) : '')
.map(x => x.startsWith('config') ? 'config:' : x)
.map(x => x.endsWith(':app') ? ':app' : x)
.pop();
Note that the above could we converted using function composition:
const ret =
pipe(
ifElse(file_exists, file_read, always('')),
when(startsWith('config'), always('config:')),
when(endsWith(':app'), always(':app')))
(file)
Of course one could argue that you execute unnecessary checks but unless a performance issue has been identified, I'd always favour readability over anything else.
Can we improve readability here? We certainly can try:
const ret =
[file]
.map(load_file_content)
.map(when_starts_with('config'))
.map(when_ends_with(':app'))
.pop();
Or
const ret =
pipe(
load_file_content,
when_starts_with('config'),
when_ends_with(':app'))
(file)
I find this readable but others may not so 🤷‍♂️

Besides the ternary operator (which probably isn't gonna make things more legible), have you considered early returns?
if (!file_exists(file)) {
return '';
}
content = file_read(file);
if (content.startsWith('config')) {
return 'config:';
}
if (content.endsWith(':app')) {
return ':app';
}
return ...;
There's still gonna be just as much branching logic behind the scenes, but this way you can logically unentangle semantically different code blocks from each other.

Related

Check if array value is included in string

I'm working on some client side validation for a contact form of sorts, the website currently isn't online so server side isn't relevant.
I am trying to create a 'word filter' to catch on any abusive of obscene language before the form is 'submitted'.
Heres the code, without the obscenities...
function filterInput(str) {
var inputFilter = ['word1', 'word2', 'word3'];
var arrayLength = inputFilter.length;
if (inputFilter.indexOf(str) > - 1) {
// Word caught...
} else {
// Clear...
}
If the user were to enter 'word1', it will catch the word. If the user enters 'word1word2' or 'John is a word3', it doesn't catch it.
I originally had a for loop which worked better, but still wouldn't work without whitespace between words('word1word2').
Any input would be greatly appreciated, I've been searching but nothing quite matches my needs.
EDIT: So I too have come up with a solution, but seeing the varying ways this can be achieved I am curious as to how it works and also why a particular way is better?
Heres what I came up with...
function filterInput(str) {
var inputFilter = ['word1', 'word2', 'word3'];
var arrayLength = inputFilter.length;
for (var i = 0; i < arrayLength; i++) {
if (str.includes(inputFilter[i])) {
window.alert('Message...');
return;
}
}
}
You're looking for some rather than indexOf, since you have to do custom matching:
if (inputFilter.some(function(word) { return str.indexOf(word) != -1; })) {
// Word caught...
} else {
// Clear...
}
Or with an ES2015+ arrow function and String.prototype.includes:
if (inputFilter.some(word => str.includes(word))) {
// Word caught...
} else {
// Clear...
}
some calls the callback repeatedly until the first time it returns a truthy value. If the callback ever returns a truthy value, some returns true; otherwise, some returns false. E.g., it's asking if "some" of the entries match the predicate function. (any may have been a better term, but when adding to the built-ins, the TC39 committee have to do a lot of work to avoid conflicts with libraries and such.)
If you ever need to get back the actual entry, use find which returns the entry or undefined if not found. If you need its index, use findIndex.
Side note: Just beware that it's notoriously complicated to do this well. Beware of the Scunthorpe problem, and of course people will routinely just confuse the sequence of letters or substitute asterisks or similar to defeat filters of this sort...
you can try something like this:-
function filterInput(str) {
var badWords = ['bad', 'worst'];
var isTrue = false;
if(str) {
for (var i = 0; i < badWords.length; i++) {
isTrue = !!(str.replace(/\W|\s/g, '').toLowerCase().indexOf(badWords[i]) + 1);
if(isTrue) break;
}
}
return isTrue;
}

Extend the number class

I want to extend the number class to have instance functions such as odd and even so I can do something like this:
2.odd() => false
2.even() => true
1.even() => false
1.odd() => true
Extending classes is a good Ruby practise: "Ruby check if even number, float".
Is the same true in JavaScript, or does it cause performance issues or some other problem?
Anyway, I can't extend despite my best efforts:
var NumberInstanceExtensions = {
accuracy: function(){
return 'This is cool ' + this
}
}
$.extend(Number.prototype,NumberInstanceExtensions);
alert( $.type(5) ); //-> number
//alert( 5.accuracy() ); //-> Uncaught SyntaxError: Unexpected token ILLEGAL
http://jsfiddle.net/VLPTb/2/
How can I get this to work? The syntax error makes me think this isn't how JavaScript works on a fundamental level. Is my best bet extending the Math class and doing this instead:
Math.odd(2) => false
Math.even(2) => true
Math.even(1) => false
Math.odd(1) => true
That seems far more inelegant than 2.odd().
I think as long as you understand the side-effects of your "extension" then you're okay. I often modify the String prototype to add an "elipsis" method so I can do things like
"SomeString".elipsis()
But start at the beginning. You're not "extending classes" in JavaScript. JavaScript is a prototype-based language. You can modify prototypes to do what you need.
You won't be able to add a method directly to the number itself. You can, however modify the prototype of the Number object:
Number.prototype.even = function(){
return this.valueOf() % 2 === 0;
}
With this, you won't be able to use the following syntax:
10.even();
But, since you aren't hard-coding stuff, otherwise you wouldn't need this function anyways, you CAN do the following:
var a = 10;
a.even(); //true
I might say that you could consider adding a utilities object to do these things, because modifying primitive prototypes is not always guaranteed to be side-effect free.
This function does not really provide any gain for you. You're checking for odd and even, replacing one line of code with another. Think about the difference:
var a = 10;
var aIsEven = a.even();
vs:
var a = 10;
var aIsEven = a % 2 === 0;
You gain three characters of code, and the second option is less likely to break your "JavaScript".
You can extend natives JS objects by using (for example) Number.prototype.myFn = function(){}.
So you could do :
Math.prototype.odd = function(n){
return n % 2 === 0;
};
Math.prototype.even = function(n){
return n % 2 === 1;
};
And then use it like so :
var two = 2;
console.log(Math.odd(2)); // true
BUT I would strongly advise you against extending natives in JavaScript.
You can read more about it here
EDIT : After trying my code on JSFiddle, it appears the Math object has no prototype, you can read more about it here. The code above won't work !
Instead, you could do :
Math.odd = function(n){
return n % 2 === 0;
};
Math.even = function(n){
return n % 2 === 1;
};
console.log(Math.odd(2)); // true
or :
Number.prototype.odd = function(){
return this % 2 === 0;
};
Number.prototype.even = function(){
return this % 2 === 1;
};
console.log(new Number(2).odd()); // true
I'd like to point out that that is already available in the numbers class.
Just use the boolean methods, odd? and even?
2.odd?
=> false
2.even?
=> true
Hope this helps.
No need to create a new class, it already exists in the numbers class.

Avoiding having to write the same word over and over again

I'm very new to javascript so this question might sound stupid. But what is the correct syntax of replacing certain words inside variables and functions. For example, I have this function:
function posTelegram(p){
var data = telegramData;
$("#hotspotTelegram").css("left", xposTelegram[p] +"px");
if (p < data[0] || p > data[1]) {
$("#hotspotTelegram").hide()
} else {
$("#hotspotTelegram").show()
}
};
There is the word "telegram" repeating a lot and every time I make a new hotspot I'm manually inserting the word to replace "telegram" in each line. What would be a smarter way of writing that code so that I only need to write "telegram" once?
Group similar / related data in to data structures instead of having a variable for each bit.
Cache results of calling jQuery
Use an argument
function posGeneral(p, word){
// Don't have a variable for each of these, make them properties of an object
var data = generalDataThing[word].data;
// Don't search the DOM for the same thing over and over, use a variable
var hotspot = $("#hotspot" + word);
hotspot.css("left", generalDataThing[word].xpos[p] +"px");
if (p < data[0] || p > data[1]) {
hotspot.hide()
} else {
hotspot.show()
}
};
You can't always avoid this kind of repetition (this is general to all programing languages).
Sometimes, you can make generic functions or generic classes, for example a class which would embed all your data :
Thing = function(key, xpos) {
this.$element = $('#hotspot'+key);
this.xpos = xpos;
};
Thing.prototype.pos = function (p, data) {
this.$element.css("left", this.xpos[p] +"px");
if (p < this.data[0] || p > this.data[1]) {
this.$element.hide()
} else {
this.$element.show()
}
};
And we could imagine that this could be called like this :
var telegramThing = new Thing('telegram', xposTelegram);
...
telegramThing.pos(p, data);
But it's really hard to make a more concrete proposition without more information regarding your exact problem.
I recommend you read a little about OOP and javascript, as it may help you make complex programs more clear, simple, and easier to maintain.
For example, using a Thing class here would enable
not defining more than once the "#hotspotTelegram" string in your code
reusing the logic and avoid making the same code with another thing than "telegram"
not having the Thing logic in your main application logic (usually in another Thing.js file)
But don't abstract too much, it would have the opposite effects. And if you don't use objects, try to keep meaningful variable names.
var t = "Telegram";
var $_tg = $('#hotspotTelegram');
$_tg.css("left", "xpos"+t[p] + "px"); // not sure about this line, lol
$_tg.hide();
$_tg.show();
etc.
you can create a selector as variable, something like this
function posTelegram(p){
var data = telegramData;
var $sel = $("#hotspotTelegram");
$sel.css("left", xposTelegram[p] +"px");
if (p < data[0] || p > data[1]) {
$sel.hide()
} else {
$sel.show()
}
};

How can I parse the first JSON object on a stream in JS

I have a stream of JSON objects, as with JSON-RPC over TCP or WebSockets. There's no length prefix or delimiter, because JSON is self-delimiting. So, when I read from the stream, I may end up with something like this:
{"id":1,"result":{"answer":23},"error":null}
{"id":2,"result":{"answer":42},"error":null}
{"id":3,"result":{"answ
I need to parse each JSON object one by one. I can't do this with JSON.parse, because it will just throw a syntax error for extraneous data at the end.
Of course with that example I could go line by line, but I can't rely on the whitespace looking like that; JSON-RPC can just as easily look like this:
{
"id": 1,
"result": {
"answer": 23
},
"error":null
}
Or this:
{"id":1,"result":{"answer":23},"error":null}{"id":2,"result":{"answer":42},"error":null}
With most parsers in other languages, the obvious answer is something like this (using Python as an example):
buf = ''
decoder = json.JSONDecoder()
def onReadReady(sock):
buf += sock.read()
obj, index = decoder.raw_decode(buf)
buf = buf[index:]
if obj:
dispatch(obj)
But I can't find anything similar in JS. I've looked at every JS parser I can find, and they're all effectively equivalent to JSON.parse.
I tried looking at various JSON-RPC frameworks to see how they handle this problem, and they just don't. Many of them assume that a recv will always return exactly one send (which works fine for JSON-RPC over HTTP, but not over TCP or WebSockets—although it may appear to work in local tests, of course). Others don't actually handle JSON-RPC because they add requirements on whitespace (some of which aren't even valid for JSON-RPC).
I could write a delimiter check that balances brackets and quotes (handling escaping and quoting, of course), or just write a JSON parser from scratch (or port one from another language, or modify http://code.google.com/p/json-sans-eval/), but I can't believe no one has done this before.
EDIT: I've made two versions myself, http://pastebin.com/fqjKYiLw based on json-sans-eval, and http://pastebin.com/8H4QT82b based on Crockford's reference recursive descent parser json_parse.js. I would still prefer to use something that's been tested and used by other people rather than coding it myself, so I'm leaving this question open.
After a month of searching for alternatives and not finding anything useful, I decided to code up a bunch of different implementations and test them out, and I went with my modification of Crockford's reference recursive-descent parser (as described in the question, available here).
It wasn't the fastest, but it was more than fast enough in every test I did. More importantly, it catches clearly erroneous JSON, when that's not ambiguous with incomplete JSON, much better than most of the other alternatives. Most importantly, it required very few, and pretty simple, changes from a well-known and -tested codebase, which makes me more confident in its correctness.
Still, if anyone knows of a better library than mine (and just being used by lots of projects instead of just me would count as a major qualification), I'd love to know about it.
Here is a simple JSON Object separator. It assumes that you receive a series of JSON objects (not array) and that are well formed.
function JSONObjectSepaator() {
this.onObject = function (JSONStr) {};
this.reset = function () {
this.brace_count = 0;
this.inString = false;
this.escaped = false;
this.buffer = "";
};
this.receive = function (S) {
var i;
var pos=0;
for (i = 0; i < S.length; i++) {
var c = S[i];
if (this.inString) {
if (this.escaped) {
this.escaped = false;
} else {
if (c == "\\") {
this.escaped = true;
} else if (c == "\"") {
this.inString = false;
}
}
} else {
if (c == "{") {
this.brace_count++;
} else if (c == "}") {
this.brace_count--;
if (this.brace_count === 0) {
this.buffer += S.substring(pos,i+1);
this.onObject(this.buffer);
this.buffer = "";
pos=i+1;
}
} else if (c == "\"") {
this.inString = true;
}
}
}
this.buffer += S.substring(pos);
};
this.reset();
return this;
}
To use it, you can do it this way:
var separator = new JSONObjectSepaator();
separator.onObject = function (o) {
alert("Object received: "+o);
};
separator.receive('{"id":1,"result":{"answer":23},"error":null, "x');
separator.receive('x":"\\\""}{"id":2,"result":{"answer":42},"error":null}{"id":');
separator.receive('3,"result":{"answer":43},"err{or":3}');
Pragmatic answer: use python
pos = 0
try:
while not pos == len(str(body)):
# raw_decode will parse as much of the line as possible and return how much was left
j, json_len = decoder.raw_decode(str(body)[pos:])
pos += json_len
# "j" holds your object
I'm writing some code to parse large numbers of JSON files saved from streams which have from 1 to 50 JSON objects in each one. This is a JavaScript hack but it works for my purposes (relies on the error message containing the position of the "bad" JSON. If the error character is a "{" then I know it's probably the start of a new object):
let contents = ""; // your JSON string
let startIndex = 0;
let endIndex = contents.length;
do {
let obj;
try {
let str = contents.slice(startIndex, endIndex);
obj = JSON.parse(str);
startIndex = endIndex;
endIndex = contents.length;
console.log("successfully parsed", obj);
}
catch (e) {
let match = e.toString().match(/^SyntaxError: Unexpected token { in JSON at position (\d+)$/);
if (match && match.length == 2) {
endIndex = parseInt(match[1]) + startIndex;
}
else throw e;
}
} while (startIndex < endIndex);

shorter conditionals in js

I have conditionals like this:
if (foo == 'fgfg' || foo == 'asdf' || foo == 'adsfasdf') {
// do stuff
}
Surely there's a faster way to write this?
Thanks.
You might consider a switch-case statement
switch(foo) {
case "fgfg":
case "asdf":
case "adsfasdf":
// ...
}
It's not really any shorter, but could be more readable depending on how many conditions you use.
I would keep the conditionals the way they are. Any clever way of shortening them would make the code less idiomatic and less readable.
Now, if you do care about readability, you could define a function to do the comparison:
if( foo_satisfies_condition(foo) ) {
// ...
}
Or:
if( is_month_name(foo) {
// ...
}
If you give the function a name that faithfully describes what it does, it will be easier to understand the intent of the code.
How you implement that function would depend on how many comparisons you need. If you have a really large number of strings you're comparing against, you could use a hash. The implementation details are irrelevant when reading the calling code, though.
if (/^(fgfg|asdf|adsfasdf)$/.test(foo)) {
or:
if (["fgfg", "asdf", "adsfasdf"].indexOf(foo) != -1) {
Cross-browser support for Array.indexOf is still limited. Also, these are faster to write, probably not faster to run.
No need for using indexOf or a regex if you just use a hash table:
var things = { 'fgfg' : 1, 'asdf' : 1, 'asdfasdf' : 1 };
if ( things[foo] ) {
...
}
Here's a easy way:
String.prototype.testList = function(lst) {
lst = lst.split('|');
for(var i=0; i<lst.length; i++){
if (this == lst[i]) return true;
}
return false;
};
To use this function, you can just do this:
if (foo.testList('fgfg|asdf|adsfasdf')) {
You can also rename testList to whatever you want, and change the delimiter from | to anything you want.
Depending on the situation you could do..
//At some point in your code
var vals = new Array('fgfg', 'asdf', 'adsfasdf');
//...
if(vals.indexOf(foo) >= 0)
Ternary operator looks good if you like and has else
Use tilde (~) for a shorter expression
if (~["fgfg", "asdf", "adsfasdf"].indexOf(foo)) {
//do stuff
}

Categories

Resources