Summarize Replace Function In Javascript - javascript

Is there a way to summarize a replace function and make the code a bit cleaner? I haven't found a way to do this yet and cannot find an answer on here.
replaceFunction(string) {
this.string = (encodeURIComponent(string.toLowerCase()
.replace('/%[a-fA-F0-9]{2}/','-')
.replace('/-+/','-')
.replace('/-$/','')
.replace('/^-/','')
.replace('ä','ae')
.replace('ö','oe')
.replace('ü','ue')
.replace('Ä','ae')
.replace('Ö','oe')
.replace('Ü','ue')
.replace('_','-')
.replace('.','-')
.replace(/\s/g, '-')
.replace(/["']/g, '')
));
return string;
}

You could try something similar to this (which is just a start, not the full implementation). This approach would also make it unnecessary to iterate over the string several times.
// First insert the polyfill posted here: https://stackoverflow.com/a/4314050/10406502
let a = 'Häberle-+Hänsle';
for(let i = 0; i < a.length; i++) {
switch (a[i]) {
case '-':
if (i + 1 < a.length && a[i + 1] !== '+')
break;
a = a.splice(i + 1, 1, '');
i--;
break;
case 'ä':
a = a.splice(i, 1, 'ae');
i++;
break;
}
}

Related

Remove Last Character From String If It's a "!" Using For-Loop - JavaScript

I have
function remove(s) {
for (i = 0; i < s.length; i++) {
let lastChar = s.slice(-1);
if (lastChar === "!") {
s = s.substring(0, s.length - 1);
}
else {
return s;
}
}
return s;
}
And this is passing 105 tests but failing 1 on codewars.
The test that it's failing is:
Expected: '\'isl\'', instead got: '\'isl!\'' for when (s) is "isl!!!!!"
I can't figure out why, in this case, it's not removing the last character in the string.
This should be removing the last character in the string whenever it's !:
if (lastChar === "!") {
s = s.substring(0, s.length - 1);
}
I've also tried:
s = s.replace("!", "");
But same result. Any ideas?
Because you're increasing i and checking i < s.length on each loop. At one point, you remove a ! (thus shortening the string) and i is equal to s.length and you never check the last char.
There's no reason for i at all. (Or a for loop, but if that was the requirement in the challenge...)
If you step through it with your debugger, you'll see the problem. This version using console.log also shows the problem:
function remove(s) {
for (i = 0; i < s.length; i++) {
let lastChar = s.slice(-1);
if (lastChar === "!") {
s = s.substring(0, s.length - 1);
console.log(`i = ${i}, s = '${s}', s.substring(i) = '${s.substring(i)}'`);
}
else {
console.log(`returning '${s}'`);
return s;
}
}
console.log(`returning '${s}' at end, because ${i} >= ${s.length}`);
return s;
}
remove("isl!!!!!");
.as-console-wrapper {
max-height: 100% !important;
}
You can do this without using for loop.
const stringRemover (str) => {
if (str[str.length-1] === "!") {
return str.slice(0,str.length-1);
} else {
return str;
}
}
You can create a recursive function and check if the last char using CharAt if it is !. If it is so then again call the same function but with new string which is created after removing the last !
Not sure why the for is needed if the last character is needed
function remove(str) {
let getLastChar = str.charAt(str.length - 1);
if (getLastChar === '!') {
return remove(str.substring(0, str.length - 1))
} else {
return str;
}
}
console.log(remove("isl!!!!!"));
Here is codewars result
Here is result
As answered in a previous reply, i < s.length is checked in every iteration in a for loop.
Try this :
function remove(s) {
let a = s.length;
for (i = 0; i < a; i++) {
let lastChar = s.slice(-1);
if (lastChar === "!") {
s = s.substring(0, s.length - 1);
}
else {
return s;
}
}
return s;
}
#T.J. Crowder pointed me in the right direction, but he didn't provide an answer that followed my original logic (in this case I wanted to use a for-loop).
The key takeaway is that s = s.replace("!", ""); will work when i-- and s = s.replace(/!+$/g, '') will work when i++. Because, as far as I understand, the replace() method only replaces the first occurrence of the string, which is why we need i-- to force the loop to iterate backwards through the string, making sure that every occurance of "!" gets replaced.
I.e. this will work:
function remove(s) {
for (i = 0; i < s.length; i--) {
let lastChar = s.slice(-1);
if (lastChar === "!") {
s = s.replace("!", '')
}
else {
return s;
}
}
return s;
}
And this will also work:
function remove(s) {
for (i = 0; i < s.length; i++) {
let lastChar = s.slice(-1);
if (lastChar === "!") {
s = s.replace(/!+$/g, '');
}
else {
return s;
}
}
return s;
}

How can I fix my code in order to determine if the entire string is alphabetical?

I'm learning Javascript and trying to make a function that compares a current letter of a string with the one succeeding it, repeating through the entire string to then determine if it is alphabetical. My if condition is what's wrong, but I'm not sure how I'd change it to make it work.
Also, I'd like to keep the structure similar to what I have if possible.
function is_alphabetical(string) {
let result;
for ( i = 0; i < string.length; i++ ) {
if ( string[i] <= string[i + 1] ) {
result = true;
} else {
result = false;
break;
}
}
return result;
}
Have a look into localCompare function e.g.
"a".localeCompare("b")
>> -1
"a".localeCompare("a")
>> 0
"b".localeCompare("a")
>> 1
in your case:
if((string[i + 1]).localCompare(string[i]))
Hope that helps :)
By keeping the structure similar as you asked, I think this solution works:
function is_alphabetical(string) {
for ( i = 0; i < string.length; i++ ) {
if ( string[i] >= string[i + 1] ) {
return false;
}
}
return true;
}
Here's how I would do it.
function isAlphabetical(str) {
// we iterate only while i<length-1 to avoid going out of bounds when doing str[i+1]
for (let i = 0, n = str.length - 1; i < n; i++) {
if (str[i] > str[i + 1])
return false; // at least one letter is making it not ordered
// else keep checking other letters
}
return true; // if we reached here, its ordered
}

decodeString codefights: Program doesn't pass all tests."30/31 Output limit exceeded on test -31". Kindly support

function decodeString(s)
{
let arr = [];
let digitSum = '';
let digitSumArr = []; // for numbers before '['
let i;
//iterating string
for (i = 0; i < s.length; i++)
{
if (!isNaN(s[i]))
{
digitSum += s[i]; // count number before '['
}
else if (s[i] === '[')
{
// add number to the array
digitSumArr.push(+digitSum);
arr.push(i + 1);
digitSum = '';
}
else if (s[i] === ']')
{
let digit = digitSumArr.pop();
i = decStr(arr, i, digit);
digitSum = '';
}
else
{
digitSum = '';
}
}
return s;
function decStr(arr, j, number)
{
let arrLen = arr.length;
let n = number;
let str = s.slice(arr[arrLen - 1], j);
let sumStr = str;
while (n-- > 1)
{
sumStr = sumStr.concat(str);
}
str = number + '[' + str + ']';
s = s.replace(str, sumStr);
arr.splice(arrLen - 1, 1);
//return position for iterating
return j + sumStr.length - str.length - 1;
}
}
Given an encoded string, return its corresponding decoded string.
The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is repeated exactly k times.
Note: k is guaranteed to be a positive integer.
Note that your solution should have linear complexity because this is what you will be asked during an interview.
The problem is that the failed test has an input of sufficient complexity to require more time to solve than the allotted limit, given your solution. So, you need to find a more efficient solution.
I ran a performance benchmark on your solution and on another solution which used recursive procedure calls, and yours was 33% slower. I suggest you refactor your solution to call your parsing procedure recursively when you encounter nested iterations.

regex to match dots but not when enclosed by nested square brackets

input
books.copies.[read_by.[p_id="65784"].page=5468].text.[paragraph="20"].letters
the idea is to split the string by dots but ignore those inside square brackets
so after splitting there should be an array
[
'books',
'copies',
'[read_by.[p_id="65784"].page=5468]',
'text',
'[paragraph="20"]',
'letters'
]
I already looked at this answer but it doesn't work with nested square brackets, which is what i need. Also I'm using javascript, so negative lookbehinds are not supported.
Help is much appreciated.
Edit 1: expand example
It isn't possible to do it with a regex in Javascript that isn't able to match nested structures. You need to use the good old method: a stack.
var text = 'books.copies.[read_by.[p_id="65784"].page=5468].text.[paragraph="20"].letters';
var item = '', result = [], stack = 0;
for (var i=0; i < text.length; i++) {
if ( text[i] == '.' && stack == 0 ) {
result.push(item);
item = '';
continue;
} else if ( text[i] == '[' ) {
stack++;
} else if ( text[i] == ']' ) {
stack--;
}
item += text[i];
}
result.push(item);
console.log(result);
You need to write a parser for this since a JavaScript regex does not support regex recursion, nor balanced constructs.
The point in these functions is that they keep a stack (level, openBrackets) of opening delimiters (in your case, it is [) and then check the stack state: if the stack is not emppty, the found . is considered inside the brackets, and is thus just appended to the current match. Else, when the stack is empty, the . found is considered outside of brackets, and is thus used to split on (the current value is appended to the output array (result, ret)).
function splitByDotsOutsideBrackets(string){
var openBrackets = 0, ret = [], i = 0;
while (i < string.length){
if (string.charAt(i) == '[')
openBrackets++;
else if (string.charAt(i) == ']')
openBrackets--;
else if (string.charAt(i) == "." && openBrackets == 0){
ret.push(string.substr(0, i));
string = string.substr(i + 1);
i = -1;
}
i++;
}
if (string != "") ret.push(string);
return ret;
}
var res = splitByDotsOutsideBrackets('books.copies.[read_by.[p_id="65784"].page=5468].text.[paragraph="20"].letters');
console.log(res);
Or another variation:
function splitOnDotsOutsideNestedBrackets(str) {
var result = [], start = 0, level = 0;
for (var i = 0; i < str.length; ++i) {
switch (str[i]) {
case '[':
++level;
break;
case ']':
if (level > 0)
--level;
break;
case '.':
if (level)
break;
if (start < i)
result.push(str.substr(start, i - start));
start = i + 1;
break;
}
}
if (start < i)
result.push(str.substr(start, i - start));
return result;
}
var s = 'books.copies.[read_by.[p_id="65784"].page=5468].text.[paragraph="20"].letters';
console.log(splitOnDotsOutsideNestedBrackets(s))
Adapted from one of my previous answers.

Regex to remove redundant multiplication/division involving zero or one?

I have an odd problem where I am receiving computer generated equations (as a string) where multiplications/divisions with zero or one and lone zeros are occasionally present. These equations are to be presented to a user in string form.
I know that I can remove these redundant parts of the equation by implementing a kind of parser, but I was curious as to whether a regular expression could be used to remove them.
I came up with the following before I finally gave up on my (quite limited) regex skills:
/([^\+\-]*(?:0|0\*|\*0){1,}[^\+\-]*)|(?:1\*|\*1)/g
It seems to work only if:
there are no non-zero numbers with a zero in them (ie. no 10's,20's,etc.)
there are no negations.
It also doesn't work well with parentheses. Unfortunately parentheses are quite common.
Note that removing the redundant portions stated above may result in redundant parentheses or "zero" parentheses (ie it could turn out like ()*x, which is equivalent to 0*x). The redundant parentheses are not as much of an issue, but I assume the "zero" parentheses could be removed by a second pass similar to the first looking for (). If either of these could be done in the same regex as the one that solves the problem I would be extremely impressed.
So I turn to you regex gurus of Stack Overflow. Can it be done?
Assumptions
The following can be assumed true about the stringified equations:
There are no divisions by zero, equations will not have any occurrence of [expr]/0or even expressions that evaluate to [expr]/0 such as [expr]/sin(0).
The only operators within the equations themselves are + - * and /.
Minus operator (-) includes both subtraction and negation, although negation is always surrounded by parentheses.
Any operation other than the above (sin,cos, pow, etc.) will appear as a function call. (no ^ %, etc.)
Sample Equation
"(0+(0/0.5+(0+1*cos(p)+0*0+0*sin(p))*cos(k)+(0+1*0+0*1+0*0)*(-sin(k))+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*x+(0+(0+1*cos(p)+0*0+0*sin(p))*sin(k)+(0+1*0+0*1+0*0)*cos(k)+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*y+(0+(0+1*cos(p)+0*0+0*sin(p))*0+(0+1*0+0*1+0*0)*0+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*1)*z)"
Quite cluttered isn't it?
After leaving the comment I couldn't resist having a crack at it :)
You're biggest problem is nested parentheses. Regexes themselves are really bad at handling nested structures. This is a prime example of my mantra "Regular expressions are a tool, not a solution".
With regexes as your tool, you can apply kind of a "leaf-first" (or bottom-up) approach for this tree-structure, that's what I do in the first part while (sEq.match(...)) {...}. After that I can walk through the created array and do some simple text edits.
I've also added that 1*, *1 and /1 are deleted as they similarly don't affect the equation. You could probably expand this to make it smart enough to replace sin(0)/cos(0) with 0 and 1 respectively, then the solution would be even smaller in some cases.
(As mentioned in the comments of the code, this breaks if the equation contains stuff like 5.0*4 because JavaScript regexes don't have lookbehind so I'm trusting the \b word boundary to do that work for me. Simply adding logic that deletes unnecessary decimals would solve this though. Something like sEq = sEq.replace(/\.0+\b/g, ''); but I don't know if that's necessary for your use-case.) Edit: now fixed, 5.0*4 should remain in tact
This is not thoroughly tested though, feedback welcome.
var sEq = "(0+(0/0.5+(0+1*cos(p)+0*0+0*sin(p))*cos(k)+(0+1*0+0*1+0*0)*(-sin(k))+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*x+(0+(0+1*cos(p)+0*0+0*sin(p))*sin(k)+(0+1*0+0*1+0*0)*cos(k)+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*y+(0+(0+1*cos(p)+0*0+0*sin(p))*0+(0+1*0+0*1+0*0)*0+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*1)*z)";
var aParts = [];
document.getElementById('output').value = sEq + '\n';
while (sEq.match(/\([^()]*\)/)) {
// while there are still "leafs", save them to aParts and replace with
// a reference to their index in aParts, making their parent a new
// "leaf" because it now doesn't contain the round brackets anymore
sEq = sEq.replace(/([a-z]*)\(([^()]*)\)/gi, function(m, f, a) {
var n = aParts.length;
aParts[n] = {
'found':m,
'funct':f,
'arith':a
};
return '[' + n + ']';
});
}
for (var i = 0; i < aParts.length; i++) {
// isolate divisions/multiplications
var dms = aParts[i]['arith'].split(/(?=[+-])/);
for (var j = 0; j < dms.length; j++) {
// if the isolated part is multiplied by or divided into 0, replace with 0
if (dms[j].match(/([^.]|^)\b0[*\/]|\*0(?!\.?0*[1-9])/)) {
dms[j] = dms[j].replace(/([+-]?).*/, '$1'+'0');
}
// remove /1, *1 and 1*
dms[j] = dms[j].replace(/[\/*]1\b(?!\.0*[1-9])(?:\.0*)?/g, '').replace(/([^.]|^)\b1\*/g, '$1');
}
// join back together, remove 0+, +0 and -0; 0- results in negation
aParts[i]['arith'] = dms.join('').replace(/[+-]0(?!\.?0*[1-9])(?:\.?0*)?/g, '').replace(/([^.]|^)\b0(?:\+|(-))/g, '$1$2');
// if after this the part contains just 0, perpetuate down to further eliminate
if (aParts[i]['funct']=='' && aParts[i]['arith']=='0') {
for (var j = i + 1; j < aParts.length; j++) {
if (aParts[j]['arith'].indexOf('[' + i + ']') != -1) {
aParts[j]['arith'] = aParts[j]['arith'].replace('[' + i + ']', '0');
break;
}
}
}
// add back parts previously simplified by replacing [n] with the content of aParts[n]
aParts[i]['arith'] = aParts[i]['arith'].replace(/\[(\d+)\]/g, function (m, n) {
return aParts[parseInt(n)]['funct'] + '(' + aParts[parseInt(n)]['arith'] + ')';
});
// This is just to show the progress of the algorithm
document.getElementById('parts').value += i + '\t' + aParts[i]['found'] + '\n';
var tmp = [];
for (var a = 0; a < aParts.length; a++) {
tmp[a] = {
'funct':aParts[a]['funct'],
'arith':aParts[a]['arith'].replace(/\[(\d+)\]/g, function (m, n) {
return tmp[parseInt(n)]['funct'] + '(' + tmp[parseInt(n)]['arith'] + ')';
})
};
}
// some steps didn't change after analysing, only append when significant
if (document.getElementById('output').value.indexOf('\n' + tmp[tmp.length-1]['arith'] + '\n') ==-1)
document.getElementById('output').value += tmp[tmp.length-1]['arith'] + '\n';
}
document.getElementById('solution').innerHTML =
aParts[aParts.length-1]['funct'] +
'(' + aParts[aParts.length-1]['arith'] + ')';
<h3>Parts isolated:</h3>
<textarea id="parts" rows="10" style="width:100%" wrap="off"></textarea>
<h3>Steps that simplified the equation:</h3>
<textarea id="output" rows="10" style="width:100%" wrap="off"></textarea>
<h3>Solution:</h3>
<pre id="solution"></pre>
I ended up implementing a completely non-regex, recursive approach to the problem. The cleanupEqn() function essentially splits each string by operators (top level parentheses are grouped as a single operand), recursively operates on each sub part, then does another run through on the way back up the function call chain.
Comparing this with funkwurm's regex solution in jsperf shows it is significantly faster (at least in my personal chrome and firefox browsers).
It hasn't been thoroughly tested yet, and I'm sure there could be improvements made so I welcome any feedback.
Stealing funkwurm's snippet display to show my solution:
var sEq = "(0+(0/0.5+(0+1*cos(p)+0*0+0*sin(p))*cos(k)+(0+1*0+0*1+0*0)*(-sin(k))+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*x+(0+(0+1*cos(p)+0*0+0*sin(p))*sin(k)+(0+1*0+0*1+0*0)*cos(k)+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*y+(0+(0+1*cos(p)+0*0+0*sin(p))*0+(0+1*0+0*1+0*0)*0+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*1)*z)";
var operators = ['+','-','*','/'];
var level = 0;
var result = cleanupEqn(sEq);
document.getElementById('solution').innerHTML = result;
function cleanupEqn(eqn){
var parts = removeRedundant(splitByParen(eqn));
level++;
document.getElementById('output').value += 'Level ' + level + ': Processing ' + eqn + '\n';
for(var i=0; i < parts.length; i++){
document.getElementById('parts').value += parts[i] + '\n';
if(parts[i].charAt(0) === '('){
// Clean up the expression inside the parentheses
var tmp = cleanupEqn(parts[i].substring(1,parts[i].length-1));
// If it was reduced to a zero, don't add the parentheses back
if(tmp === '0'){
parts[i] = '0';
}
else {
parts[i] = '(' + tmp + ')';
}
}
}
// Finally, remove redundancies again, since some might have bubbled up.
removeRedundant(parts);
document.getElementById('output').value += 'Level ' + level + ': Completed ' + eqn + '\n' + JSON.stringify(parts, null, '\t') + '\n';
level--;
// Join it all into a string and return
return parts.join('');
}
function splitByParen(str){
var out = [];
var exprStart = 0;
var count = 0;
var i;
for (i = 0; i < str.length; i++) {
var t = str.charAt(i);
if(str.charAt(i) === '('){
if(i > exprStart && count === 0){
out.push(str.substring(exprStart, i));
exprStart = i;
}
count++;
}
else if(str.charAt(i) === ')'){
count--;
if(count === 0){
out.push(str.substring(exprStart, i+1));
exprStart = i+1;
}
}
else if(count === 0 && operators.indexOf(str.charAt(i)) > -1){
if(i > exprStart){
out.push(str.substring(exprStart, i));
}
out.push(str.charAt(i));
exprStart = i+1;
}
}
// Add the last part
if(i > exprStart){
out.push(str.substring(exprStart, i));
}
return out;
}
function removeRedundant(parts) {
for(var i=0; i < parts.length; i++){
if(parts[i] === '0'){
if(i === 0){
switch(parts[i+1]){
case '*':
case '/':
if(parts[i+1] === '*' || parts[i+1] === '/'){
parts.splice(i, 3, '0');
}
else {
parts.splice(i, 2);
}
i--;
break;
case '+':
parts.splice(i, 2);
i--;
break;
case '-':
parts.splice(i, 1);
i--;
}
}
else {
switch(parts[i-1]){
case '*':
if(parts[i+1] === '*' || parts[i+1] === '/'){
// Check if the prior portion is part of a function call
if(i > 2 && operators.indexOf(parts[i-3]) < 0){
// Check if the next portion is part of a function call (or undefined)
if(i+3 < parts.length && operators.indexOf(parts[i+3]) < 0){
parts.splice(i-3, 6, '0');
i -= 4;
}
else {
parts.splice(i-3, 5, '0');
i -= 4;
}
}
else {
parts.splice(i-2, 4, '0');
i -= 3;
}
}
else {
parts.splice(i-2, 3, '0');
i -= 3;
}
break;
case '+':
case '-':
if(parts[i+1] === '*' || parts[i+1] === '/'){
// Check if the next portion is part of a function call (or undefined)
if(i+3 < parts.length && operators.indexOf(parts[i+3]) < 0){
parts.splice(i, 4, '0');
}
else {
parts.splice(i, 3, '0');
}
i--;
}
else if(parts[i+1] === '+'){
parts.splice(i-1, 2);
i -= 2;
}
else {
parts.splice(i-1, 2);
i -= 2;
}
}
}
}
else if(parts[i] === '1'){
if(i === 0){
switch(parts[i+1]){
case '*':
parts.splice(i, 2);
i--;
break;
case '+':
case '-':
if(parts[i+1] === '*'){
parts.splice(i, 2);
i--;
}
}
}
switch(parts[i-1]){
case '*':
case '/':
if(parts[i+1] !== '/'){
parts.splice(i-1, 2);
i -= 2;
}
break;
case '+':
case '-':
if(parts[i+1] === '*'){
parts.splice(i, 2);
i--;
}
}
}
}
return parts;
}
<h3>Parts isolated:</h3>
<textarea id="parts" rows="10" style="width:100%" wrap="off"></textarea>
<h3>Steps that simplified the equation:</h3>
<textarea id="output" rows="10" style="width:100%" wrap="off"></textarea>
<h3>Solution:</h3>
<pre id="solution"></pre>
<script src="new.js"></script>

Categories

Resources