Code Refactoring. Trying to improve my code - javascript

My code passed, no problem. But I would like your guys opinion as to what I could have improved in my code. Unnecessary things, tips, better ways to do the same thing, faster ways, I'm literally open to any kind of feedback. Lately I'm only trying to focus on improve how fast I can solve a problem, and this one, took me almost 5 hours.
This code comes from the 2D Array HourGlass.
My thought process was to makeup a model of what I wanted, than for loop through the lines and rows, and that's how I came with this result.
Also, I wanted to improve from thinking of WHAT the code should do, other than HOW. It's hard, but any tips I would really appreciate.
Since I'm coding only Front End stuff, my solving problems is literally shit.
Thanks !
function hourglassSum(arr) {
let newInput = arr
let arrAnswer = []
for(let line in newInput){
for (let row in newInput){
let newRow = parseInt(row)
let newLine = parseInt(line)
if(newLine < 4){
let a =newInput[newLine +0][newRow]
let b =newInput[newLine +0][newRow+1]
let c =newInput[newLine +0][newRow+2]
let d =newInput[newLine +1][newRow+1]
let e =newInput[newLine +2][newRow]
let f =newInput[newLine +2][newRow+1]
let g =newInput[newLine +2][newRow+2]
if(a,b,c,d,e,f,g == undefined){
break
}
arrAnswer.push([a,b,c,d,e,f,g].reduce((item1,item2)=> item1 + item2, 0))
}
}
}
let answer = arrAnswer.reduce((item1, item2) => (item1 > item2 ) ? item1: item2 )
return answer
}

if(a,b,c,d,e,f,g == undefined) Are you expecting this to check if any of your 7 values are undefined?
Based on the comma operator specs I believe it is only checking g == undefined.
The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.
If you really mean to check for any null values, here's one way you could do it
if([a,b,c,d,e,f,g].indexOf(undefined)>=0) ...

Your code has a lot of redundancies:
let newInput = arr
Unnecessary.
let answer = arrAnswer.reduce((...
Stuffing it in a var is unnecessary, since you just return it on the next line.
As far as I can tell, your entire code can be changed to the following:
const hourglassSum = input => {
return input
.map((a, i, arr) => { // NEVER use for..in with arrays. Use .map or for..of
return arr.map(b => {
const idx1 = parseInt(a, 10); // always use radix
const idx2 = parseInt(b, 10);
// Use boolean short-circuiting, we'll filter later.
// Your original code had potentially error throw here
// if the first access resulted in undefined.
const intermediate = input[idx1] !== undefined &&
input[idx1 + 1] !== undefined &&
input[idx1 + 2] !== undefined &&
[
input[idx1][idx2],
input[idx1][idx2 + 1],
input[idx1][idx2 + 2],
input[idx1 + 1][idx2 + 1],
input[idx1 + 2][idx2],
input[idx1 + 2][idx2 + 1],
input[idx1 + 2][idx2 + 2],
];
// boolean again, check to make sure we don't pollute the
// arithmetic
return intermediate &&
intermediate.every(x => x !== undefined) &&
intermediate;
})
.filter(x => x) // weed out falses
.reduce((a, b) => a + b, 0); // sum to int
})
.reduce((a, b) => Math.max(a, b)); // Math.max replaces ternary
};
This is arguably more readable, definitely less error prone, slightly shorter, makes better use of the built-ins like Math.max and the array methods. Is also consistent rather than mixing functional style with loops. One thing it isn't is faster, but you make it correct first, then fast.

Related

Why can't I reassign this variable inside an if statement using Javascript? [duplicate]

This question already has answers here:
What is the difference between ( for... in ) and ( for... of ) statements?
(18 answers)
Closed 1 year ago.
I am experiencing some weird behaviour that I think is scope related, but I can't explain why it's happening.
So this is the problematic piece of code. I'm trying to Camel Case some words and differentiate between text separated by dashes and underscores. Ignore the fact that the algorithm isn't completely correct yet, that's not the point. This is the stage I noticed the problem.
function toCamelCase(str){
let isDash = false
for (let char in str) {
if (char === "-") {
isDash = true
}
}
if (isDash) {
return str.split("-").map(word => word[0].toUpperCase() + word.slice(1, word.length + 1)).join("")
} else {
return str.split("_").map(word => word[0].toUpperCase() + word.slice(1, word.length + 1)).join("")
}
}
let result = "why-false"
console.log(toCamelCase(result))
So isDash determines which type of separator does the text contain. I can obviously use an arrow function instead of this approach, and it would work perfectly. Something like this:
let isDash = () => {
for (let char in str) {
if (char === "-") {
return true
}
But I can't understand why doesn't the previous example work as well. I've read about scope and it should be possible to reassign to that variable if it was defined at a higher level. Even if I defined isDash outside the function, it still didn't work. It's value always remains the initial one: false. Why isn't the if statement changing its value to true?
let x = [1, 4, 5, 7]
let txt = 0
let working = "no"
for (let i in x) {
txt += x[i]
if (txt > 4) {
working = "yes"
}
}
console.log(working)
As you can see, if I write something similar to demonstrate if it's possible to use the variable in a nested if statement, it works fine and working will log yes. What am I missing here? I would really appreciate some help, this really is a dead end to me and I wouldn't have asked for help if I could've figured it out myself. Big thanks to anyone that can explain this stuff to me!
The issue here is your for loop:
for (let char in str) {
if (char === "-") {
isDash = true
}
}
The for-in loop loops over indices or keys, not values (you can test this by doing console.log(char); in the loop). Instead, use a for-of loop.
The issue is that you're using the for...in statement, which loops over enumerable property names of an object.
for (let char in str) {
if (char === "-") {
isDash = true
}
}
You can use for...of statement to iterate over Strings and use const if you are not going to reassign the variable inside the block.
This way:
for (const char of str) {
if (char === "-") {
isDash = true
}
}
I'm fairly certain the reason for this is your use of 'let' instead of 'var'. (To be honest I hadn't seen it used before this post...).
See this article:
https://www.google.com/amp/s/www.geeksforgeeks.org/difference-between-var-and-let-in-javascript/amp/
And if this fixes your issue, have a read up further on the difference s between the two.
I personally would always just use var .

Improve string - prefix matching performance

I'm looking for a way to speed up my naive string matching process:
// Treat this as pseudo code
function find(input: string, prefixes: string[]) {
for (let i = 0; i < prefixes.length; i++) {
const prefix = prefixes[i];
if (input.startsWith(prefix)) {
return prefix;
}
}
return null;
}
const prefixes = [ "Hey", "Hi", "Hola", ... ];
const prefix = find("Hey, I'm Michael", prefixes);
I've looked into some probabilistic data structures like the bloom filter but I couldn't find one that'd fit my needs. This being said, I don't actually care to get the prefix that would have matched neither do I need a 100% guarantee that a match exists. I only need to know if the input does definitely NOT contain any prefix or that it might.
I've also come across an article about a Burst Tries algorithm which as far as I could understand will serve a similar purpose. Frankly, though I'm not deep enough into algorithms to grasp the full implementation details and make sure this is what I'm looking for.
Side note:
I assume that 99.95% of the input this function will be getting is not going to match any prefix. Therefore I would like for this to be an optimization step to only process strings that will likely have a prefix I'm looking for.
Any help or suggestions would be very much appreciated :3
If the prefixes are known in advance and can be preprocessed, you might try a trie. Especially if they are going to be as short as 10 characters. That would mean each check is on the order of 10 comparisons. Not sure how much better one could do.
function buildTrie(trie, words){
for (let word of words){
let _trie = trie;
for (let i=0; i<word.length; i++){
const letter = word[i];
_trie[letter] = _trie[letter] || {};
if (i == word.length - 1)
_trie[letter]['leaf'] = true;
_trie = _trie[letter];
}
}
return trie;
}
function find(trie, str, i=0){
const letter = str[i];
if (!trie[letter])
return false;
if (trie[letter]['leaf'])
return true;
return find(trie[letter], str, i + 1);
}
const prefixes = [ "Hey", "Heya", "Hi", "Hola"];
const trie = buildTrie({}, prefixes)
console.log(trie)
console.log(find(trie, "Hey, I'm Michael"));
console.log(find(trie, "Heuy, I'm Michael"));
This has no logical difference from the answer by גלעד ברקן, but it displays working with a trie in a quite different code style. (It also uses $ instead of leaf as a terminator; a Symbol would be a good alternative.)
const trie = (words) =>
words .reduce (insertWord, {})
const insertWord = (trie, [c, ...cs]) =>
c ? {...trie, [c]: insertWord (trie [c] || {}, cs)} : {...trie, $: 1}
const hasPrefix = (trie) => ([c, ...cs]) =>
'$' in trie ? true : c ? c in trie && hasPrefix (trie [c]) (cs) : true
const testPrefixes = (prefixes) =>
hasPrefix (trie (prefixes))
const hasGreeting = testPrefixes (["Hey", "Hi", "Hola", "Howdy"])
console .log (hasGreeting ("Hey, I'm Michael"))
console .log (hasGreeting ("Hello, Michael. I'm Michelle"))
console .log (trie ((["Hey", "Hi", "Hola", "Howdy"])))
.as-console-wrapper {max-height: 100% !important; top: 0}
testPrefixes accepts a list of prefixes and returns a function that will report on whether a string starts with one of those prefixes. It does this by creating a trie and partially applying it to hasPrefix. Internally, the trie is built by folding insertWord over an initial empty object.
Of course this only makes sense if your use-case has prefixes that are reused for multiple calls. If not, I see little better than const testPrefixes = (prefixes) => (word) => prefixes .some ((pfx) => word .startsWith (pfx))
For search for a lot of possible substrings in the string, you can use idea from Rabin-Karp algorithm.
In my program Banmoron I used this algorithm for select malicious requests by search substring. See sources on github.
Edit: It makes sense that startsWith is faster than indexOf, I struggle to find benchmarks comparing the two, the one I did find depends on browser speed and chrome runs indexOf faster than startsWith, I would love to know more on this. Below is the original answer:
I have taken a lot to .indexOf recently for most of these cases. The performance, from what I have read up, is better than most loop cases and allows for easy use, here is an example of two functions:
findOne: Returns the first entry that is found (breaks the loop which can increase performance).
findAll: Loops through all prefixes and returns an array of the
found prefixes
If you are specifically looking for prefixes (thus the index value is equal to 0, just change the function to represent it with
input.indexOf(prefixes[i]) === 0
instead of
input.indexOf(prefixes[i]) >= 0
Here is the code snippet:
const exampleString = "Hello, I am Michael, bey👋";
const examplePrefixes = ["Hello", "Holla", "bey👋"];
function findOne(input, prefixes) {
// Loop through prefixes to test if is in the string
for (let i = 0; i < prefixes.length; i++) {
// If the prefix does not exist in the string it returns a value of -1
if (input.indexOf(prefixes[i]) >= 0) {
// Retrun the prefix value if it is found
return prefixes[i];
}
}
// Return null if nothing is found
return null
}
function findAll(input, prefixes) {
// Initialize return array
let retArr = [];
// Loop through prefixes to test if is in the string
for (let i = 0; i < prefixes.length; i++) {
// If the prefix does not exist in the string it returns a value of -1
if (input.indexOf(prefixes[i]) >= 0) {
// If the prefix exists, push it onto the return array
retArr.push(prefixes[i]);
}
}
// return the array after looping through each prefix
return retArr.length !==0 ? retArr : null
}
let value1 = findOne(exampleString, examplePrefixes);
let value2 = findAll(exampleString, examplePrefixes);
console.log(value1); // Hello
console.log(value2); // [ 'Hello', 'bey👋' ]

How to check if less than 2 of 3 variables are empty

I need to check that there are at least 2 values before running a script, but can't seem to get the condition to fire.
When I use if (risForm) {... the script runs when risForm is filled, and when I use if (!(risForm)) {... the script runs if risForm is empty, but I can't seem to work out how to check if any 2 of the three is full... I've tried this:
if ((!(risForm)) + (!(runForm)) + (!(angForm)) < 2) {...
along with a numerous adjustments to precise formatting/bracketting, but it's not getting me anywhere!
Make an array of the variables, filter by Boolean, then check the length of the array:
const forms = [risForm, runForm, angForm];
if (forms.filter(Boolean).length < 2) {
throw new Error('Not enough forms are filled');
}
// rest of the code
You can avoid creating an intermediate array by using reduce instead, if you wanted:
const filledFormCount = forms.reduce((a, form) => a + Boolean(form), 0);
if (filledFormCount < 2) {
throw new Error('Not enough forms are filled');
}
If you can have all your variables inside an array, you can do
yourArray.filter(Boolean).length >= 2
To break it apart, let's rewrite the above in a more verbose fashion:
yourArray
.filter(
function (variable) {
return Boolean(variable)
}
)
.length >= 2
Now, array.filter() gets every variable in the array and runs each as the argument for the function inside the parens, in this case: Boolean(). If the return value is truthy, the variable is "filtered in", if not it is "filtered out". It then returns a new array without the variables that were filtered out.
Boolean() is a function that will coerce your value into either true or false. If there's a value in the variable, it will return true... But there's a catch: it will return false for zeroes and empty strings - beware of that.
Finally, we use .length to count how many variables were "filtered in" and, if it's more than two, you can proceed with the code.
Maybe this pseudo code can illustrate it better:
const variables = ['foo', undefined, 'bar'];
variables.filter(Boolean).length >= 2;
['foo', undefined, 'bar'].filter(Boolean).length >= 2;
keepIfTruthy(['foo' is truthy, undefined is falsy, 'bar' is truthy]).length >= 2;
['foo', 'bar'].length >= 2;
2 >= 2;
true;
Javascript's true and false are useful here because when coerced to a number, they become respectively 1 and 0. So...
function foo(a,b,c) {
const has2of3 = !!a + !!b + !!c;
if ( has2of3 ) {
// Do something useful here
}
}
One caveat, though is that the empty string '' and 0 are falsy, which means they would be treated as not present. If that is an issue, you could do something like this:
function foo(a,b,c) {
const hasValue = x => x !== undefined && x !== null;
const has2of3 = hasValue(a) + hasValue(b) + hasValue(c);
if ( has2of3 ) {
// Do something useful here
}
}
let risForm = "a",
runForm = "",
angForm = "";
let arr = [risForm, runForm, angForm]
let res = arr.filter(el => !el || el.trim() === "").length <= 1
console.log(res)
There many ways to solve this. Lets try with basic idea. You want to make a reusable code and something that support multiple variables, also condition value might change. This means, we need to define an array of the form values and a condition value to verify. Then we can apply a function to verify the condition. So let's try some code:
let risForm, runForm, angForm;
risForm = 'Active';
// runForm = 3;
const formValues = [risForm, runForm, angForm];
const minFilledForms = 2;
const hasValue = a => !!a && a !== undefined && a !== null;
verifyForms();
function verifyForms() {
let filledForms = formValues.reduce((count, form) => count + hasValue(form), 0);
if (filledForms < minFilledForms) {
console.log(`Only ${filledForms} of ${formValues.length} forms have filled. Minimum ${minFilledForms} forms are requiered.`);
}
console.log('Continue...');
}

Longest common subsequence (Why does this recursive solution not work?)

Trying to write a similar recursive solution to the one described on: http://www.geeksforgeeks.org/longest-common-subsequence/ but it does not work. It outputs one. Anyone have an idea of why?
LCS_seq_req = (str1, str2) => {
m=str1.length;
n=str2.length;
str1_cut = str1.slice(0, m-1)
str2_cut = str2.slice(0, n-1)
if (m===0 || n===0) {
return 0
}
else if (str1.slice(m-1, m) === str2.slice(n-1, n) ) {
return LCS_seq_req(str1_cut, str2_cut) + 1
} else {
res_1 = LCS_seq_req(str1_cut, str2)
res_2 = LCS_seq_req(str1,str2_cut)
return Math.max(res_1, res_2)
}
}
LCS_seq_req("AGGTAB", "GXTXAYB")
In JavaScript, unlike (say) Python, assigning to a variable inside a function does not implicitly declare it as a local variable. Instead, you need to explicitly declare it using the var keyword; otherwise you get a global variable.
More specifically, your problem is that this line:
res_1 = LCS_seq_req(str1_cut, str2)
has the side-effect of mutating the global variable str2_cut, causing this line:
res_2 = LCS_seq_req(str1,str2_cut)
to compute the wrong value. If you add var in the right places, you'll get the right answer.
Incidentally, Eric Lippert has written a blog post, https://ericlippert.com/2014/03/05/how-to-debug-small-programs/, which gives very good advice for how to debug this sort of problem on your own.
I looked at the Naive recursive Python implementation of LCS problem you give and converted Python code into JS code. Hope it will help.
LCS_seq_req = (str1, str2, m, n) => {
if(m == 0 || n == 0)
return 0;
else if(str1.charAt(m-1) === str2.charAt(n-1))
return 1 + LCS_seq_req(str1, str2, m-1, n-1);
else
return Math.max(LCS_seq_req(str1, str2, m, n-1), LCS_seq_req(str1, str2, m-1, n));
}
var X = "AGGTAB";
var Y = "GXTXAYB";
console.log(LCS_seq_req(X , Y, X.length, Y.length)); //6

Using array.splice inside Array prototype

Array.prototype.move = function(oldIndex, newIndex) {
var val = this.splice(oldIndex, 1);
this.splice(newIndex, 0, val[0]);
}
//Testing - Change array position
var testarray = [1, 2, 3, 4];
testarray.move(3, 0);
console.log(testarray);
This produces an error "this.splice is not a function" yet it returns the desired results. Why?
Array.prototype.move = function(oldIndex, newIndex) {
if(Object.prototype.toString.call(this) === '[object Array]') {
if(oldIndex && typeof oldIndex == 'number' && newIndex && typeof newIndex == 'number') {
if(newIndex > this.length) newIndex = this.length;
this.splice(newIndex, 0, this.splice(oldIndex, 1)[0]);
}
}
};
For some reason, the function is being called by the called by the document on load (still haven't quite figured that one out). I added a few checks to verify that this = an array, and then also reset the new index to be equal to the total size if the supplied int was greater than the total length. This solved the error issue I was having, and to me is the simplest way to move objects around in an array. As for why the function is being called onload must be something to do with my code.
You don't need the placeholder variable-
Array.prototype.move = function(oldIndex, newIndex) {
this.splice(newIndex, 0, this.splice(oldIndex, 1)[0]);
}
var a=[1,2,3,4,9,5,6,7,8];
a.move(4,8);
a[8]
/* returned value: (Number)
9
*/
Adding properties to built–in objects is not a good idea if your code must work in arbitrary environments. If you do extend such objects, you shouldn't use property names that are likely to be used by someone else doing the same or similar thing.
There seems to be more than one way to "move" a member, what you seem to be doing can be better named as "swap", so:
if (!Array.prototype.swap) {
Array.prototype.swap = function(a, b) {
var t = this[a];
this[a] = this[b];
this[b] = t;
}
}
I expect that simple re-assignment of values is more efficient than calling methods that need to create new arrays and modify the old one a number of times. But that might be moot anyway. The above is certainly simpler to read and is fewer characters to type.
Note also that the above is stable, array.swap(4,8) gives the same result as array.swap(8,4).
If you want to make a robust function, you first need to work out what to do in cases where either index is greater than array.length, or if one doesn't exist, and so on. e.g.
var a = [,,2]; // a has length 3
a.swap(0,2);
In the above, there are no members at 0 or 1, only at 2. So should the result be:
a = [2]; // a has length 1
or should it be (which will be the result of the above):
a = [2,,undefined]; // a has length 3
or
a = [2,,,]; // a has length 3 (IE may think it's 4, but that's wrong)
Edit
Note that in the OP, the result of:
var b = [,,2];
b.move(0,2);
is
alert(b); // [,2,];
which may not be what is expected, and
b.move(2,0);
alert(b); // [2,,];
so it is not stable either.

Categories

Resources