Swapping consecutive/adjacent characters of a string in JavaScript - javascript

Example string: astnbodei, the actual string must be santobedi. Here, my system starts reading a pair of two characters from the left side of a string, LSB first and then the MSB of the character pair. Therefore, santobedi is received as astnbodei. The strings can be a combination of letters and numbers and even/odd lengths of characters.
My attempt so far:
var attributes_ = [Name, Code,
Firmware, Serial_Number, Label
]; //the elements of 'attributes' are the strings
var attributes = [];
for (var i = 0; i < attributes_.length; i++) {
attributes.push(swap(attributes_[i].replace(/\0/g, '').split('')));
}
function swap(array_attributes) {
var tmpArr = array_attributes;
for (var k = 0; k < tmpArr.length; k += 2) {
do {
var tmp = tmpArr[k];
tmpArr[k] = tmpArr[k+1]
tmpArr[k+1] = tmp;
} while (tmpArr[k + 2] != null);
}
return tmpArr;
}
msg.Name = attributes; //its to check the code
return {msg: msg, metadata: metadata,msgType: msgType}; //its the part of system code
While running above code snippet, I received the following error:
Can't compile script: javax.script.ScriptException: :36:14 Expected : but found ( return {__if(); ^ in at line number 36 at column number 14
I'm not sure what the error says. Is my approach correct? Is there a direct way to do it?

Did you try going through the array in pairs and swapping using ES6 syntax?
You can swap variables like this in ES6:
[a, b] = [b, a]

Below is one way to do it. The code you have is not valid because return is not allowed outside a function.
let string = "astnbodei";
let myArray = string.split('');
let outputArray = [];
for (i=0; i<myArray.length; i=i+2) {
outputArray.push(myArray[i+1]);
outputArray.push(myArray[i]);
}
console.log(outputArray.join(''));

Consecutive pairwise character swapping of/within a string very easily can be solved by a reduce task ...
function swapCharsPairwise(value) {
return String(value)
.split('')
.reduce((result, antecedent, idx, arr) => {
if (idx % 2 === 0) {
// access the Adjacent (the Antecedent's next following char).
adjacent = arr[idx + 1];
// aggregate result while swapping `antecedent` and `adjacent`.
result.push(adjacent, antecedent);
}
return result;
}, []).join('');
}
console.log(
'swapCharsPairwise("astnbodei") ...',
swapCharsPairwise("astnbodei")
);

The reason for the error in the question was the placement of the function swap. However, switching its placement gave me another error:
java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: javax.script.ScriptException: delight.nashornsandbox.exceptions.ScriptCPUAbuseException: Script used more than the allowed [8000 ms] of CPU time.
#dikuw's answer helped me partially. The following line of code worked for me:
var attributes_ = [Name, Code,
Firmware, Serial_Number, Label
]; //the elements of 'attributes' are the strings
var attributes = [];
for (var i = 0; i < attributes_.length; i++) {
attributes.push(swap(attributes_[i].replace(/\0/g, '').split('')));
}
return {msg: msg, metadata: metadata,msgType: msgType};
function swap(array_attributes) {
var tmpArr = [];
for (var k = 0; k < array_attributes.length; k= k+2) {
if( (array_attributes[k + 1] != null)) {
tmpArr.push(array_attributes[k+1]);
tmpArr.push(array_attributes[k]);
}}
return tmpArr.join('');
}

Related

How to find an unknown pattern, that is present in multiple strings?

I'm trying to find a part in multiple strings, that all strings share in common. For example:
const string1 = '.bold[_ngcontent="_kjhafh-asda-qw"] {background:black;}';
const string2 = '[_ngcontent="_kjhafh-asda-qw"] {background-color:hotpink;}';
const string3 = 'div > p > span[_ngcontent="_kjhafh-asda-qw"] {background:hotpink;}'
I don't know in advance what exactly the string is that I'm looking for, so I have to loop over the strings and find out. In the example above, the pattern would be [_ngcontent="_kjhafh-asda-qw"].
Is this even possible? Also, it would have to understand that maybe no such pattern exists. And are there methods for that or do I need to implement such an algorithm myself?
EDIT (context): We are building a validator, that checks a micro-frontend for global CSS rules (not prefixed and outside a shadow-dom), by loading it in isolation in a headless browser (within a jenkins pipeline) and validate, that it should not break any other stuff by global rules, that might be outside the context of the micro-frontend, on the same page. Using a headless browser, we can make use of the document.styleSheets property and not miss any styles that are being loaded. This will find <style> tags and its contents, aswell as content of external stylesheets.
Leveraging the BLAST algorithm, the following code snippet seeks successively matching substrings.
//
// See https://stackoverflow.com/questions/13006556/check-if-two-strings-share-a-common-substring-in-javascript/13007065#13007065
// for the following function...
//
String.prototype.subCompare = function(needle, haystack, minLength) {
var i,j;
haystack = haystack || this.toLowerCase();
minLength = minLength || 5;
for (i=needle.length; i>=minLength; i--) {
for (j=0; j <= (needle.length - i); j++) {
var substring = needle.substr(j,i);
var k = haystack.indexOf(substring);
if (k !== -1) {
return {
found : 1,
substring : substring,
needleIndex : j,
haystackIndex : k
};
}
}
}
return {
found : 0
}
}
//
// Iterate through the array of strings, seeking successive matching substrings...
//
strings = [
'.bold[_ngcontent="_kjhafh-asda-qw"] {background:black;}',
'[_ngcontent="_kjhafh-asda-qw"] {background-color:hotpink;}',
'div > p > span[_ngcontent="_kjhafh-asda-qw"] {background:hotpink;}'
]
check = { found: 1, substring: strings[ 0 ] }
i = 1;
while ( check.found && i < strings.length ) {
check = check.substring.subCompare( strings[ i++ ] );
}
console.log( check );
Note that without seeing a larger sampling of string data, it's not clear whether this algorithm satisfies the objective...
Thanks to Trentium's answer, I was able to do it. I adapted the code a little bit, as it was doing too much and also, the substr didn't yield a consistent result (it depended on the order of input strings).
The code could obviously be further minified/simplified.
const findCommonPattern = (base, needle, minLength = 5) => {
const haystack = base.toLowerCase();
for (let i = needle.length; i >= minLength; i--) {
for (let j = 0; j <= needle.length - i; j++) {
let prefix = needle.substr(j, i);
let k = haystack.indexOf(prefix);
if (k !== -1) {
return {
found: true,
prefix,
};
}
}
}
return {
found: false,
};
};
const checkIfCssIsPrefixed = (strings) => {
let check = { found: true };
let matchingStrings = [];
for (let i = 1; check.found && i < strings.length; ++i) {
check = findCommonPattern(strings[0], strings[i]);
matchingStrings.push(check.prefix);
}
// Sort by length and take the shortest string, which will be the pattern that all of the strings share in common.
check.prefix = matchingStrings.sort((a, b) => a.length - b.length)[0];
return check;
};
console.log(
checkIfCssIsPrefixed([
".spacer[_ngcontent-wdy-c0]",
"[_nghost-wdy-c0]",
".toolbar[_ngcontent-wdy-c0]",
"p[_ngcontent-wdy-c0]",
".spacer[_ngcontent-wdy-c0]",
".toolbar[_ngcontent-wdy-c0] img[_ngcontent-wdy-c0]",
"h1[_ngcontent-wdy-c0], h2[_ngcontent-wdy-c0], h3[_ngcontent-wdy-c0], h4[_ngcontent-wdy-c0], h5[_ngcontent-wdy-c0], h6[_ngcontent-wdy-c0]",
".toolbar[_ngcontent-wdy-c0] #twitter-logo[_ngcontent-wdy-c0]",
".toolbar[_ngcontent-wdy-c0] #youtube-logo[_ngcontent-wdy-c0]",
".toolbar[_ngcontent-wdy-c0] #twitter-logo[_ngcontent-wdy-c0]:hover, .toolbar[_ngcontent-wdy-c0] #youtube-logo[_ngcontent-wdy-c0]:hover",
])
);

Problems with finding unique values in regEx

I have a javascript regEx that is supposed to find all values with curly brackets around them eg {} and return a list of the unique values. I thought that it was working perfectly but I found that it doesn't work depending on the sequence of values.
For example: If the target document contains {lorem}{lorem}{ipsem}{ipsem} the script logs what's wanted [lorem, ipsem] but {lorem}{ipsem}{ipsem}{lorem} the script logs [lorem, ipsem,lorem]. What am I doing wrong!?
function getVariables() {
var doc = DocumentApp.getActiveDocument();
var str = doc.getText(); //get the text of the document
var result = str.match(/{.*?}/g).map(function(val) {
return val.replace(/[\])}[{(]/g, "");
//return val.replace(/(^.*\[|\].*$)/g,'');
});
//The purpose of sort_unique is to find one of every value or string represented in an array
function sort_unique(arr) {
if (result.length === 0) return arr;
arr = arr.sort(function(a, b) {
return a * 1 - b * 1;
});
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i - 1] !== arr[i]) {
ret.push(arr[i]);
}
}
for (var index = 0; index < ret.length; index++) {
Logger.log(ret[index]);
}
return ret;
}
result = sort_unique(result);
Logger.log("Getting final result for front end....");
Logger.log(result);
return result;
}
I believe part of your problem is the sort method. If you replace
arr = arr.sort(function(a, b) {
return a * 1 - b * 1;
});
with
arr = arr.sort();
Then the function appears to work, at least on my side.
This will run in O(n log n) time. You can do better without sorting, if you store the values you've found so far in a map instead of an array. This would run in linear time.
(Also you'll want to replace if (result.length === 0) return arr; with if (arr.length === 0) return arr; just to make your sort_unique function completely independent of the surrounding function.)
The simplest method would be to use a Set. Store each of the regex matches in a set, then return Array.from(mySet).
var mySet = new Set();
str.match(/{.*?}/g).forEach(function(val) {
mySet.add(val.replace(/[\])}[{(]/g, ""));
});
return Array.from(mySet);
A set's add() function is O(1) so the total running time is O(n) where n is the number of matches in your string. Though, realistically, the regex search will be where most of the processing time occurs.
You check if the subsequent items are the same and those that are not subsequent land in the resulting array.
Check if the found value is in the result, and if not add the match, else, ignore.
Use the code like
function getVariables() {
var doc = DocumentApp.getActiveDocument();
var str = doc.getText(); //get the text of the document
var m, result=[], rx = /{([^{}]*)}/g;
while (m=rx.exec(str)) {
if (result.indexOf(m[1]) == -1) {
result.push(m[1]);
}
}
result.sort(); // If you really want to sort use this
// Logger.log(result); // View the result
}
The /{([^{}]*)}/g regex matches {, then captures into Group 1 zero or more chars other than { and }. So, the value you need is in m[1]. The if (result.indexOf(m[1]) == -1) checks if the value is in result.

How do you most efficiently sort an integer variable? [duplicate]

I've seen versions of this question for other languages, but not for JS.
Is it possible to do this recursively in one function?
I understand that I need to take the first element in the string, and then append it to each solution to the recursion on the remainder of the string.
So logically, I understand how the recursion needs to go. I just don't understand how to append the first char onto each of the recursive solutions
var myString = "xyz";
function printPermut(inputString){
var outputString;
if(inputString.length === 0){
return inputString;
}
if(inputString.length === 1){
return inputString;
}
else{
for(int i = 0; i<inputString.length(); i++){
//something here like:
//outputString = outputString.concat(printPermut(inputString.slice(1))??
//maybe store each unique permutation to an array or something?
}
}
}
Let's write a function that returns all permutations of a string as an array. As you don't want any global variables, returning the permutations is crucial.
function permut(string) {
if (string.length < 2) return string; // This is our break condition
var permutations = []; // This array will hold our permutations
for (var i = 0; i < string.length; i++) {
var char = string[i];
// Cause we don't want any duplicates:
if (string.indexOf(char) != i) // if char was used already
continue; // skip it this time
var remainingString = string.slice(0, i) + string.slice(i + 1, string.length); //Note: you can concat Strings via '+' in JS
for (var subPermutation of permut(remainingString))
permutations.push(char + subPermutation)
}
return permutations;
}
To print them, just iterate over the array afterwards:
var myString = "xyz";
permutations = permut(myString);
for (permutation of permutations)
print(permutation) //Use the output method of your choice
Hope I could help you with your question.
The problem of permutations has been studied to death. Heap's algorithm is one well-known solution. Here is a version in JS, using a generator:
function *permute(a, n = a.length) {
if (n <= 1) yield a.slice();
else for (let i = 0; i < n; i++) {
yield *permute(a, n - 1);
const j = n % 2 ? 0 : i;
[a[n-1], a[j]] = [a[j], a[n-1]];
}
}
console.log(Array.from(permute("abcabad".split('')))
.map(perm => perm.join(''))
.filter((el, idx, self) => (self.indexOf(el) === idx)));
permute is designed to take and generate arrays, not strings, so we split the string into characters before calling it, and paste the characters back into strings before printing out the results.
Use Recursive Function to iterate through the string
function getPermutations(string) {
var results = [];
if (string.length === 1)
{
results.push(string);
return results;
}
for (var i = 0; i < string.length; i++)
{
var firstChar = string[i];
var otherChar = string.substring(0, i) + string.substring(i + 1);
var otherPermutations = getPermutations(otherChar);
for (var j = 0; j < otherPermutations.length; j++) {
results.push(firstChar + otherPermutations[j]);
}
}
return results;
}
var permutation = getPermutations('YES').filter((el, idx, self) => (self.indexOf(el) === idx));
console.log("Total permutation: "+permutation.length);
console.log(permutation);
Problem classification: You can look at this problem as an exploration problem, i.e., given a set of input characters explore the different ways you can arrange them.
Solution: Backtracking algorithm excels in solving exploratory problems, although it comes with high time complexity. To demonstrate a solution, imagine how you would solve this problem by hand for a small set of input characters: [a, b, c].
Here are the steps:
Take the left most character. This is the character at index 0 and swap it with target right character at index 0, i.e. with itself. This is because [a, b, c] is a valid permutation on its own therefore we want to keep it. Swapping characters normally requires two pointers which point to each of the characters. So let's say we will have a left and right pointer.
With the same left most character (at index 0) do the swapping with target right character at index 0 + 1 = 1, i.e. move the target right pointer with 1 step further. This will give you the output: [b, a, c]
With the same left most character (at index 0) do the swapping with the next next target right character (i.e. index 0 + 1 + 1 = 2). This will give you the output: [c, b, a]
Ok, now we need to stop as there are no more target right characters to be swapped with the left most character. So our right pointer needs to stay less than the max index in the input. Moving the right pointer with a step at a time we can do with a for loop which starts from the left index and ends with the input length - 1.
Now you need to do exact same steps from above but move the left pointer so that it points to the next left most character. However, keeping the input from step 2 and 3. Another way to imagine this situation is to say: 'Hey, I am done with the left most character. Now I do not want to work with it anymore but I would love to continue with the second left most from the results I have so far.'
When do we stop? When the left pointer has reached the length of the input string - 1, 'cause there is no more characters after this index. In recursive algorithms (such as the backtracking), the case where you need to stop is called base case. In our example the base case is: left === input.length - 1.
Here is a graphical visualisation:
left index| Input String:
-------------------------------------------------------------------------------
left = 0 | in=[a, b, c]
(swap in[0] with in[0]) (swap in[0] with in[1]) (swap in[0] with in[2])
left = 1 | in=[a, b, c] in=[b, a, c] in=[c, b, a]
(swap in[1] with in[1]) (swap in[1] with in[2]) (swap in[1] with in[1])(swap in[1] with in[2]) (swap in[1] with in[1])(swap in[1] with in[2])
left = 2 | [a, b, c] [a, c, b] [b, a, c] [b, c, a] [c, b, a] [c, a, b]
Summary:
To move the left pointer to the right we will use recursive increment
To move the right pointer to the right we will use a for loop, however we need to start always from the left pointer or else we will explore things we have already explored.
Backtracking:
A pseudo-code for backtracking algorithm takes the form of:
fun(input)
if(base_case_check(input)) {
//do final step
} else {
//choose
fun(reduce(input)) //explore
//un-choose
}
Our solution:
function permutate(string) {
if(!string || string.length === 0)
return new Set(['']);
let left = 0;
let result = new Set();
permutationHelper(string, result, left);
return result;
}
function permutationHelper(string, result, left) {
if(left === string.length-1) {
//base case
result.add(string);
} else {
//recursive case
for(let right=left; right < string.length; right++) {
string = swap(string, left, right); //choose
permutationHelper(string, result, left+1); // explore
string = swap(string, left, right); //unchoose
}
}
}
function swap(string, left, right) {
let tmpString = string.split('');
let tmp = tmpString[left];
tmpString[left] = tmpString[right];
tmpString[right] = tmp;
return tmpString.join('');
}
/* End of solution */
/* Tests */
let input = 'abc';
let result = permutate(input);
let expected = new Set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba']);
if(setsEquality(result, expected)) {
console.log('Congrats, you generated all permuations');
} else {
console.log('Sorry, not all permuations are generated');
}
function setsEquality(actualResult, expectedResult) {
if (actualResult.size !== expectedResult.size) {
return false;
}
for (let permutation of actualResult) {
if (!expectedResult.has(permutation)) return false;
}
return true;
}
function assert(condition, desc) {
if (condition) {
console.log(`${desc} ... PASS`);
} else {
console.log(`${desc} ... FAIL`);
}
}
Summary & Time Complexity:
We make our choice by swapping characters in the existing input string
We explore what is left to be explored once we increment our left index with 1. This in fact means that we are reducing our input set for all subsequent recursions with 1. Therefore the work we need to do is: Nx(N-1)x(N-2)x(N-3)x...x1 = N!. However, as we needed a for loop to explore among the input we have, the total time complexity would be: 0(N*N!)
We revert our choice by swapping characters back in the modified input string
permutation=(str,prefix)=>{
if(str.length==0){
console.log(prefix);
}
else{
for(let i=0;i<str.length;i++){
let rem = str.substring(0,i)+str.substring(i+1);
permutation(rem,prefix+str[i]);
}
}
}
let str="ABC";
permutation(str,"");
Semi-Off topic:
random permutation of a given string is as simple as rndperm:
i = document.getElementById("word");
b = document.getElementById("butt");
rndperm = (z) => {
return z.split("").sort(() => ((Math.random() * 3) >> 0) - 1).join("")
}
function scramble() {
i.value = rndperm(i.value);
}
var z;
function sci() {
if (z != undefined) {
clearInterval(z);
b.innerText = "Scramble";
z=undefined;
} else {
z = setInterval(scramble, 100);
b.innerText = "Running...";
}
}
<center><input id="word" value="HelloWorld"></input><button id="butt" onclick=sci()>Scramble</button></center>
I had same question by my interviewer last day but I was not get the correct logic then I came to stackoverflow and I get here but now I have my solution and want to share with all
const str_Permutations = (str,ar = []) => {
str = `${str}`; // ensure type **String**
if(ar.indexOf(str)>-1 || str.length !== (ar.strlen || str.length)) return false; // Checking if value is alreay there or(||) on recursive call string length should not be provided string
ar.strlen = ar.strlen || str.length; // Setting str length of provided value(string)
ar.push(str); // Pushing to array
for(let i = 0; i<str.length;i++){
str_Permutations(str[i] + str.split('').filter(v=>v!==str[i]).join(''),ar);
}
return Array.from(ar); // Removing *strlen* from main result and return **Result** as array
}
str_Permutations("ABC")
//Result: (6) ["ABC", "BAC", "CBA", "BCA", "ACB", "CAB"]
There is used reference feature of Array to hold the values in same Array by passing. I hope you got my point!!!!
const permut = (str) => {
if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str];
return str
.split("")
.reduce(
(acc, letter, i) =>
acc.concat(
permut(str.slice(0, i) + str.slice(i + 1)).map((val) => letter + val)
),
[]
);
};
found here
This does the job, recursively
function printPermutations(str, res='') {
if (!str.length){
console.log(res);
}
for (let i = 0; i < str.length; i++) {
let remStr = str.substr(0, i) + str.substr(i + 1);
printPermutations(remStr, res + str.substr(i, 1));
}
}
printPermutations("abc")
// result
// abc, acb, bac, bca, cab, cba
Simple and readable approach but only limited to 3 chars
const stringPermutation = (str) => {
let permutations = [];
for (let i in str) {
for (let j in str) {
for (let k in str) {
if (str[i] !== str[j] && str[j] !== str[k] && str[i] !== str[k]) {
permutations.push(str[i] + str[j] + str[k]);
}
}
}
}
return permutations;
};
console.log(stringPermutation("abc"));
var str = "abcdefgh";
for(let i = 0; i<str.length; i++){
for(let j = i; j<=str.length; j++){
if(i != j){
var out = str.slice(i,j);
console.log(out);
}
}
}

How to get odd and even position characters from a string?

I'm trying to figure out how to remove every second character (starting from the first one) from a string in Javascript.
For example, the string "This is a test!" should become "hsi etTi sats!"
I also want to save every deleted character into another array.
I have tried using replace method and splice method, but wasn't able to get them to work properly. Mostly because replace only replaces the first character.
function encrypt(text, n) {
if (text === "NULL") return n;
if (n <= 0) return text;
var encArr = [];
var newString = text.split("");
var j = 0;
for (var i = 0; i < text.length; i += 2) {
encArr[j++] = text[i];
newString.splice(i, 1); // this line doesn't work properly
}
}
You could reduce the characters of the string and group them to separate arrays using the % operator. Use destructuring to get the 2D array returned to separate variables
let str = "This is a test!";
const [even, odd] = [...str].reduce((r,char,i) => (r[i%2].push(char), r), [[],[]])
console.log(odd.join(''))
console.log(even.join(''))
Using a for loop:
let str = "This is a test!",
odd = [],
even = [];
for (var i = 0; i < str.length; i++) {
i % 2 === 0
? even.push(str[i])
: odd.push(str[i])
}
console.log(odd.join(''))
console.log(even.join(''))
It would probably be easier to use a regular expression and .replace: capture two characters in separate capturing groups, add the first character to a string, and replace with the second character. Then, you'll have first half of the output you need in one string, and the second in another: just concatenate them together and return:
function encrypt(text) {
let removedText = '';
const replacedText1 = text.replace(/(.)(.)?/g, (_, firstChar, secondChar) => {
// in case the match was at the end of the string,
// and the string has an odd number of characters:
if (!secondChar) secondChar = '';
// remove the firstChar from the string, while adding it to removedText:
removedText += firstChar;
return secondChar;
});
return replacedText1 + removedText;
}
console.log(encrypt('This is a test!'));
Pretty simple with .reduce() to create the two arrays you seem to want.
function encrypt(text) {
return text.split("")
.reduce(({odd, even}, c, i) =>
i % 2 ? {odd: [...odd, c], even} : {odd, even: [...even, c]}
, {odd: [], even: []})
}
console.log(encrypt("This is a test!"));
They can be converted to strings by using .join("") if you desire.
I think you were on the right track. What you missed is replace is using either a string or RegExp.
The replace() method returns a new string with some or all matches of a pattern replaced by a replacement. The pattern can be a string or a RegExp, and the replacement can be a string or a function to be called for each match. If pattern is a string, only the first occurrence will be replaced.
Source: String.prototype.replace()
If you are replacing a value (and not a regular expression), only the first instance of the value will be replaced. To replace all occurrences of a specified value, use the global (g) modifier
Source: JavaScript String replace() Method
So my suggestion would be to continue still with replace and pass the right RegExp to the function, I guess you can figure out from this example - this removes every second occurrence for char 't':
let count = 0;
let testString = 'test test test test';
console.log('original', testString);
// global modifier in RegExp
let result = testString.replace(/t/g, function (match) {
count++;
return (count % 2 === 0) ? '' : match;
});
console.log('removed', result);
like this?
var text = "This is a test!"
var result = ""
var rest = ""
for(var i = 0; i < text.length; i++){
if( (i%2) != 0 ){
result += text[i]
} else{
rest += text[i]
}
}
console.log(result+rest)
Maybe with split, filter and join:
const remaining = myString.split('').filter((char, i) => i % 2 !== 0).join('');
const deleted = myString.split('').filter((char, i) => i % 2 === 0).join('');
You could take an array and splice and push each second item to the end of the array.
function encrypt(string) {
var array = [...string],
i = 0,
l = array.length >> 1;
while (i <= l) array.push(array.splice(i++, 1)[0]);
return array.join('');
}
console.log(encrypt("This is a test!"));
function encrypt(text) {
text = text.split("");
var removed = []
var encrypted = text.filter((letter, index) => {
if(index % 2 == 0){
removed.push(letter)
return false;
}
return true
}).join("")
return {
full: encrypted + removed.join(""),
encrypted: encrypted,
removed: removed
}
}
console.log(encrypt("This is a test!"))
Splice does not work, because if you remove an element from an array in for loop indexes most probably will be wrong when removing another element.
I don't know how much you care about performance, but using regex is not very efficient.
Simple test for quite a long string shows that using filter function is on average about 3 times faster, which can make quite a difference when performed on very long strings or on many, many shorts ones.
function test(func, n){
var text = "";
for(var i = 0; i < n; ++i){
text += "a";
}
var start = new Date().getTime();
func(text);
var end = new Date().getTime();
var time = (end-start) / 1000.0;
console.log(func.name, " took ", time, " seconds")
return time;
}
function encryptREGEX(text) {
let removedText = '';
const replacedText1 = text.replace(/(.)(.)?/g, (_, firstChar, secondChar) => {
// in case the match was at the end of the string,
// and the string has an odd number of characters:
if (!secondChar) secondChar = '';
// remove the firstChar from the string, while adding it to removedText:
removedText += firstChar;
return secondChar;
});
return replacedText1 + removedText;
}
function encrypt(text) {
text = text.split("");
var removed = "";
var encrypted = text.filter((letter, index) => {
if(index % 2 == 0){
removed += letter;
return false;
}
return true
}).join("")
return encrypted + removed
}
var timeREGEX = test(encryptREGEX, 10000000);
var timeFilter = test(encrypt, 10000000);
console.log("Using filter is faster ", timeREGEX/timeFilter, " times")
Using actually an array for storing removed letters and then joining them is much more efficient, than using a string and concatenating letters to it.
I changed an array to string in filter solution to make it the same like in regex solution, so they are more comparable.

Performance comparison with V8

I'm currently testing multiple cases for parsing lines.
Each line is formatted like that:
"dHdX5jOa7ww9cGsW7jQF=dHdX5jOa7ww9cGsW7jQF=dHdX5jOa7ww9cGsW7jQF=dHdX5jOa7ww9cGsW7jQF"
There are a lot of lines of course, and I need to extract the key, and the value.
The key is delimited by the first "=" found.
There is never a "=" char in the key.
The value is the rest of string next after the first "=" sign.
So for this exemple the result should be:
{
key: "dHdX5jOa7ww9cGsW7jQF",
value: "dHdX5jOa7ww9cGsW7jQF=dHdX5jOa7ww9cGsW7jQF=dHdX5jOa7ww9cGsW7jQF"
}
From here we can iterate on multiple solutions:
// the first one is not very efficient with split splice join method
function first(line) {
const lineSplit = line.split('='),
key = lineSplit[0],
value = lineSplit.splice(1, lineSplit.length).join('=');
return {
key,
value
};
}
// the second one execute only what i want to do
// with built-in String prototype's functions
function optimized(line) {
const index = line.indexOf("="),
key = line.substr(0, index),
value = line.substr(index + 1, line.length);
return {
key,
value
};
}
// i tried to code the logic myself
function homemade(line) {
const len = line.length;
let value = "", key = "", valued = false;
for (let i = 0; i < len; ++i) {
const char = line[i];
if (valued === false) {
if (char !== '=') {
key += char;
} else {
valued = true;
}
} else {
value += char;
}
}
return {
key,
value
};
}
// and next recode substr and foreach built-in to implemant the same
// function but with homemade substr&foreach
String.prototype.substr2 = function(from, to){
let str = "";
for (let i = from; i < to; ++i) {
str += this[i];
}
return str;
};
String.prototype.indexOf2 = function(occ){
const len = this.length;
for (let i = 0; i < len; ++i) {
if (this[i] === occ) {
return i;
}
}
return -1;
};
function overload(line) {
const index = line.indexOf2("="),
key = line.substr2(0, index),
value = line.substr2(index + 1, line.length);
return {
key,
value
};
}
And voila the results with jsBench:
[I'm using Google Chrome Version 59.0.3071.104 (Official Build) (64-bit)]
You can checkout the results of these functions with your browser in this jsBench
I don't understand what is going on. I imagined that cannot be possible since I wrote only the code i needed with native for() and other stuffs like this...
My questions are:
Why the builtin string operations are obviously much faster ?
Why this repeated string concatenation is inneficient ?
Is there an alternative to it ?
Why the builtin string operations are obviously much faster ?
Because they are optimized, and use internal implementation tricks that are not available to JavaScript code. For example, they avoid repeated string concatenation by building the result in one go.
Why this repeated string concatenation is inefficient ?
Because it creates many strings as intermediate results.
Is there an alternative to it ?
Use the builtin string operations :-)

Categories

Resources