string compression counting the repeated character in javascript - javascript

If I have a string a12c56a1b5 then out put should be a13b5c56 as character a is repeated twice so a12 becomes a13
I have tried this:
function stringCompression (str) {
var output = '';
var count = 0;
for (var i = 0; i < str.length; i++) {
count++;
if (str[i] != str[i+1]) {
output += str[i] + count;
count = 0;
}
}
console.log(output); // but it returns `a11121c15161a111b151` instead of `a13b5c56`
}

It is happening because the code is counting the occurrence of each element and appending it, even the numbers in the string.
In this code,
for (var i = 0; i < str.length; i++) {
count++;
if (str[i] != str[i+1]) {
output += str[i] + count;
count = 0;
}
}
in first iteration i = 0, str[i] = 'a' and str[i + 1] = '1' for the given string a12c56a1b5 which are not equal hence, it will generate the output as a1 for first iteration, then a111 for second iteration since str[i] = '1' and str[i + 1] = '2' now, and so on.
We can achieve this by first separating the characters from the count. Assuming, that there would be characters from a-z and A-Z only followed by the count. We can do something like this, str.match(/[a-zA-Z]+/g) to get the characters: ["a", "c", "a", "b"] and str.match(/[0-9]+/g) to get their counts: ["12", "56", "1", "5"], put them in an object one by one and add if it already exists.
Something like this:
function stringCompression(str) {
var characters = str.match(/[a-zA-Z]+/g);
var counts = str.match(/[0-9]+/g);
var countMap = {};
for (var i = 0; i < characters.length; i++) {
if (countMap[characters[i]]) {
countMap[characters[i]] += parseInt(counts[i]);
} else {
countMap[characters[i]] = parseInt(counts[i]);
}
}
var output = Object.keys(countMap)
.map(key => key + countMap[key])
.reduce((a, b) => a + b);
console.log(output);
}
stringCompression('a12c56a1b5')

Using regex to extract word characters and numbers. Keeps an object map res to track and sum up following numbers. sorts and converts back to a string.
As an example, the for-of loop iteration flow with str=a12c56a1b5:
c='a', n='12'
res['a'] = (+n = 12) + ( (res['a'] = undefined)||0 = 0)
or ie: res['a'] = 12 + 0
c='c', n='56'
res['c'] = 56 + 0
c='a', n='1'
res['a'] = 1 + (res['a'] = 12 from iteration 1.) = 13
c='b', n='5'
res['b'] = 5 + 0
thus res = { 'a': 13, 'c': 56, 'b': 5 } after the for-of loop finishes
function stringCompression (str) {
// build object map with sums of following numbers
const res = {}
for(const [,c,n] of str.matchAll(/(\w+)(\d+)/g))
res[c] = +n + (res[c]||0)
// convert object map back to string
output = Object.entries(res)
output.sort(([a],[b])=>a<b ? -1 : a>b ? 1 : 0)
output = output.map(([a,b])=>`${a}${b}`).join('')
console.log(output); // but it returns `a11121c15161a111b151` instead of `a13b5c56`
}
stringCompression('a12c56a1b5')
[,c,n] = [1,2,3] is equivalent to c=2, n=3. It is called destructuring.
matchAll matches on a regex. It's a relatively new shorthand for calling .exec repeatedly to execute a regular expression that collects all the results that the regular expression matches on.
(\w+)(\d+) is a regex for two capture groups,
\w+ is for one or more alpha characters, \d+ is for one or more digits.
for(const [,c,n] of str.matchAll...) is equivalent to:
for each M of str.matchAll...
const c = M[1], n = M[2]`
res[c]||0 is shorthand for:
"give me res[c] if it is truthy (not undefined, null or 0), otherwise give me 0"
+n uses the unary operator + to force an implicit conversion to a number. JavaScript specs for + unary makes it convert to number, since + unary only makes sense with numbers.
It is basically the same as using Number(n) to convert a string to an number.
Conversion back to a string:
Object.entries converts an object {"key":value} to an array in the form of [ [key1, value1], [key2, value2] ]. This allows manipulating the elements of an object like an array.
.sort sorts the array. I destructured the keys to sort on the keys, so "a" "b" "c" are kept in order.
.map takes an array, and "maps" it to another array. In this case I've mapped each [key,value] to a string key+value, and then taking the final mapped array of key+value strings and joined them together to get the final output.

In case it asks you to sort it alphabetically, I added #user120242's sorting code snippet to #saheb's entire answer (in between Object.keys(countMap) and .map(...). That worked for me. I tried using #user120242's whole answer, but it did not pass all the tests since it did not add the repeated letters for longer strings. But #user120242's answer did work. It just need to be sorted alphabetically and it passed all the test cases in HackerRank. I had this question for a coding assessment (called "Better Coding Compression").
P.S. I also removed checking the capital letters from #saheb's code since that wasn't required for my coding challenge.
Here's how mine looked like:
function stringCompression(str) {
var characters = str.match(/[a-zA-Z]+/g);
var counts = str.match(/[0-9]+/g);
var countMap = {};
for (var i = 0; i < characters.length; i++) {
if (countMap[characters[i]]) {
countMap[characters[i]] += parseInt(counts[i]);
} else {
countMap[characters[i]] = parseInt(counts[i]);
}
}
var output = Object.keys(countMap)
.sort(([a],[b])=>a<b ? -1 : a>b ? 1 : 0)
.map(key => key + countMap[key])
.reduce((a, b) => a + b);
console.log(output);
}
stringCompression('a12c56a1b5')

Related

How do I write a JavaScript program for the question given below

Write a function that takes three (str, c, n) arguments. Start with the end of 'str'. Insert character c after every n characters?
function three(str, c, n){
for(let i =0; i<str.length; i= i+n){
str.slice(i, i+n);
str = str + c;
}
return str;
}
console.log(three("apple", "c", 2));
I think, I am using wrong method.
Start with the end of 'str'. Insert character c after every n characters?
I assumed this means the string needs to end with c after the change. Correct me if I'm wrong.
If that's the case Array.prototype.reverse() and Array.prototype.map() can help here:
function three (str, c, n) {
return str.split('').reverse().map((currentValue, currentIndex, array) => {
if (currentIndex % n === 0) {
return currentValue + c
} else {
return currentValue
}
}).reverse().join('')
}
console.log(three("apple", "c", 2));
You can do something like that :
function three(str, c, n){
// split the string into an array
let letters = str.split('');
// copy to another array that will be the end result to avoid modifying original array
let result = [...letters];
// When we add to the array, it shift the next one and so on, so we need to account for that
let shift = 0;
// we go over each letter
letters.forEach((letter,index) => {
// if we are at the desired location
if(index > 0 && index % n == 0) {
// we add our letter at that location
result.splice(index+shift, 0, c);
// we increase shift by 1
shift++;
}
})
// we return the result by joining the array to obtain a string
return result.join('');
}
console.log(three("apple", "c", 2));//apcplce
Here, it does not work because the Array#slice does not update the actual string but returns a new string.
returns a shallow copy of a portion of an array into a new array object selected from start to end
In my opinion, the easiest way to solve your problem here would be to split the word into characters using the Array#split method, the add the char to each item if the index match the n parameters and finally, re-join the array
function three(str, c, n){
const strAsChar = str.split('')
return strAsChar.map((char, index) => (index - 1) % n === 0 ?
char + c :
char
).join('')
}
console.log(three("apple", "c", 2));

Find repeated letters in an array (javascript)

I'm new on this and javascript. I've tried to solve an exercise that consist to find repeated letter a in an array. The way to do is use basic structures (no regex neither newer ways of javascript (only ES5)). I need to do it this way to understand the bases of the language.
The output must be this:
//Captain America, the letter 'C' => 2 times.
//Captain America, the letter 'A' => 4 times.
//Captain America, the letter 'I' => 2 times.
I'm not looking for the solution, only the way to do it and its logical structures. Any suggestions are welcome.
My way but it doesn't work:
function duplicateLetter(name) {
var newArray = [];
for (var i=0; i<name.length; i++) {
console.log(name[i].indexOf(newArray));
if (name[i].indexOf(newArray) === 0) {
newArray.push(name[i]);
}
}
console.log(newArray);
//console.log(name + ", the letter '" + (newArray[0]).toUpperCase() + "' => " + newArray.length + " times");
}
duplicateLetter("Captain America");
function duplicateLetter(o) {
var arr = o.toUpperCase().split('');
var obj = {};
for(var v in arr) {
obj[arr[v]] = obj[arr[v]] || 0;
obj[arr[v]]++;
}
for(var v in obj) {
console.log(o + ", the letter '" + v + "' => " + obj[v] + ' times.');
}
}
duplicateLetter("Captain America");
The explanation:
We make the string upper case, then turn it into an array of letters.
We loop over the array, here, arr[v] becomes our letter, and:
If the key arr[v] doesn't exist in our object, we set it to 0.
We increment the value of the key arr[v] in our object (this causes obj['c'] to increment every time our letter is c. You can notice that this keeps track of the number of letters in our string.
We loop over the object v, printing the number of occurrences of each letter to console.
Note that this considers the space character as a letter. If you want an answer that doesn't, please specify so.
Here's a different answer that doesn't use objects and only counts letters (and not spaces or punctuation) to prove that everything is possible in more than one way.
// Not using objects and not counting anything but letters.
function duplicateLetter(o) {
var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var arr = o.toUpperCase().split('');
var count = [];
for(var v in arr) {
pos = letters.indexOf(arr[v]);
if(pos < 0) continue; // It wasn't a letter.
count[pos] = count[pos] || 0;
count[pos]++;
}
for(var v in count) {
if(!(count[v] > 0)) continue; // The letter never appeared.
console.log(o + ", the letter '" + letters[v] + "' => " + count[v] + ' times.');
}
}
duplicateLetter("Captain America");
I could probably also attempt an answer that doesn't use arrays at all!
Edit:
You can use for(a in b) loops to iterate arrays as well as objects. This is because an array is really just an object in which all enumerable properties have integer indices:
arr = [10,15,"hi"] is almost the same as arr = {'0' : 10, '1' : 15, '2' : "hi"} in the way Javascript works internally. Therefore, for (v in arr) will iterate over the array normally.
As requested, the same answer with normal for loops:
// Not using objects and not counting anything but letters.
function duplicateLetter(o) {
var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var arr = o.toUpperCase().split('');
var count = [];
for(var v = 0; v < arr.length; v++) {
pos = letters.indexOf(arr[v]);
if(pos < 0) continue; // It wasn't a letter.
count[pos] = count[pos] || 0;
count[pos]++;
}
for(var v = 0; v < count.length; v++) {
if(!(count[v] > 0)) continue; // The letter never appeared.
console.log(o + ", the letter '" + letters[v] + "' => " + count[v] + ' times.');
}
}
duplicateLetter("Captain America");
Note that nothing changed outside what was in the for brackets. The for-in notation is just easier for the human brain to comprehend, in my opinion, and is the reason I used it.
As for count[pos] = count[pos] || 0;, explaining why it works the way it does is extremely tedious, since it requires that you know precisely what the || operator does. So I'm just going to state what it does, without explaining it.
Basically, count[pos] = count[pos] || 0; is the same as:
if(count[pos]) { // If count[pos] evaluates to true.
count[pos] = count[pos]
} else { // count[pos] is false, '', null, undefined, 0, or any other value that evaluates to false.
count[pos] = 0;
}
Note that this works because at the start, count[pos] is undefined (count is an empty array), so it puts a 0 in it. If we find the letter again, count[pos] is defined, and is a positive value, and therefore evaluates to true, so we don't change it.
Just consider a = a || b to be equal to:
Put the default value of b into a if a is undefined (or evaluates to false by any other means).`
Make an object whose keys are the letters and values are the number of times that letter was repeated. For example,
'abbc' => {'a': 1, 'b': 2, 'c': 1}

Flipping 0's and 1's from a natural number

I want to create a javascript function to flip 1's to 0's in a natural number and I'm out of Ideas to achieve this,
Actually, I had a couple of URL's, and I replaced all 0's from a query parameter with 1's and now I no longer know the original parameter value, because there were few 1's in the original parameter value and now both are mixed, so basically I screwed myself,
The only solution for me is to try flipping each 1 to 0 and then 0's to 1's and test each number as the parameter.
This is the parameter value (after replacing 0's with 1's)
11422971
using above input I want to generate numbers as follows and test each of these
11422970
10422971
10422970
01422971
As you can see only 1's and 0's are changing, the change according to binary,
Each position in your string can be one of n characters:
A "0" can be either "0" or "1"
A "1" can be either "0" or "1"
Any other character c can only be c
We can store this in an array of arrays:
"11422971" -> [ ["0", "1"], ["0, "1"], ["4"], ... ]
To transform your string to this format, you can do a split and map:
const chars = "11422971"
.split("")
.map(c => c === "1" || c === "0" ? ["1", "0"] : [ c ]);
Once you got this format, the remaining logic is to create all possible combinations from this array. There are many ways to do so (search for "array combinations" or "permutations"). I've chosen to show a recursive pattern:
const chars = "11422971"
.split("")
.map(c =>
c === "1" || c === "0"
? ["1", "0"]
: [ c ]
);
const perms = ([ xs, ...others ], s = "", ps = []) =>
xs
? ps.concat(...xs.map(x => perms(others, s + x, ps)))
: ps.concat(s);
console.log(perms(chars));
you can do it with a number like a string, and after parse it, something like that
var number= "12551";
number= number.replace("1","0");
The result of number will be "02550"
after that parse number to int
This will generate all permutations.
const generatePermutations = (number) => {
let digits = number.split('');
// find out which digits can be flipped
let digitPositions = digits.reduce((acc, val, i) => {
if (val === '0' || val === '1') acc.push(i);
return acc;
}, []);
// we're going to be taking things in reverse order
digitPositions.reverse();
// how many digits can we flip
let noBits = digitPositions.length;
// number of permutations is 2^noBits i.e. 3 digits means 2^3 = 8 permutations.
let combinations = Math.pow(2, digitPositions.length);
let permutations = [];
// for each permutation
for (var p = 0; p < combinations; p++) {
// take a copy of digits for this permutation
permutations[p] = digits.slice();
// set each of the flippable bits according to the bit positions for this permutation
// i = 3 = 011 in binary
for (var i = 0; i < noBits; i++) {
permutations[p][digitPositions[i]] = '' + ((p >> i) & 1);
}
permutations[p] = permutations[p].join('');
}
return permutations;
};
console.log(generatePermutations('11422970'));
In case your looking for a recursive approach:
function recursive(acc, first, ...rest) {
if(!first) return acc;
if(first == '0' || first == '1') {
var acc0 = acc.map(x => x + '0');
var acc1 = acc.map(x => x + '1');
return recursive([].concat(acc0, acc1), ...rest);
} else {
return recursive(acc.map(x => x + first), ...rest);
}
}
recursive([''], ...'11422971')
// output ["00422970", "10422970", "01422970", "11422970", "00422971", "10422971", "01422971", "11422971"]
This just counts in binary and fills out a template for each value.
function getPossibleValues(str) {
function getResult(n) {
let nIndex = 0;
const strValue = n.toString(2).padStart(nDigits, '0');
return str.replace(rxMatch, () => strValue.charAt(nIndex++));
}
const rxMatch = /[01]/g;
const nDigits = str.length - str.replace(rxMatch, '').length;
const nMax = Math.pow(2, nDigits);
const arrResult = [];
for(let n = 0; n<nMax; n++) {
arrResult.push(getResult(n));
}
return arrResult;
}
console.log(getPossibleValues('11422970'));
Thank you all to respond, you saved my life, btw the approach I used was,
0- convert the number into a string. (so we can perform string operations like split())
1- count the number of 1's in the string (let's say the string is "11422971", so we get three 1's, I used split('1')-1 to count)
2- generate binary of three-digit length,(ie from 000 to 111). three came from step 1.
2- break down the string to single chars, (we'll get
array=['1','1','4','2','2','9','7','1'] )
3- take the first binary number (ie b=0b000)
4- replace first 1 from the character array with the first binary digit of b (ie replace 1 with 0), similarly replace second 1 with the second binary digit of b and so on.
5- we'll get the first combination (ie "00422970")
5- repeat step 3 and 4 for all binary numbers we generated in step 2.

How do I check each individual digit within a string?

I was given the challenge of converting a string of digits into 'fake binary' on Codewars.com, and I am to convert each individual digit into a 0 or a 1, if the number is less than 5 it should become a 0, and if it's 5 or over it should become a 1. I know how to analyze the whole string's value like so:
function fakeBin(x){
if (x < 5)
return 0;
else return 1;
}
This however, analyzes the value of the whole string, how would I go about analyzing each individual digit within the string rather than the whole thing?
Note: I have already looked at the solutions on the website and don't understand them, I'm not cheating.
You can do it in one line with two simple global string replacement operations:
function fakeBin(x){
return ("" + x).replace(/[0-4]/g,'0').replace(/[5-9]/g,'1');
}
console.log(fakeBin(1259))
console.log(fakeBin(7815))
console.log(fakeBin("1234567890"))
The ("" + x) part is just to ensure you have a string to work with, so the function can take numbers or strings as input (as in my example calls above).
Simple javascript solution to achieve expected solution
function fakeBin(x){
x = x+'' ;
var z =[];
for(var i=0;i< x.length;i++){
if((x[i]*1)<5){
z[i] =0;
}else{
z[i]=1;
}
}
return z
}
console.log(fakeBin(357))
The snippet below will take a string and return a new string comprised of zeros and/or ones based on what you described.
We use a for ...of loop to traverse the input string and will add a 0 or 1 to our return array based on whether the parsed int if greater or less than 5.
Also note that we are checking and throwing an error if the character is not a number.
const word = "1639";
const stringToBinary = function(str) {
let ret = [];
for (const char of word) {
if (Number.isNaN(parseInt(char, 10))) {
throw new Error(`${char} is not a digit!`);
} else {
const intVal = parseInt(char, 10);
ret.push(intVal > 5 ? 1 : 0);
}
}
return ret.join('');
};
console.log(stringToBinary(word));
if you are in java you can use
charAt()
and you make a for with the word length and you can check one by one
for(int i = 0; i < text.length(); i++){
yourfunction(texto.charAt(i));
}
Split the string and apply the current function you have to each element of the string. You can accomplish this with map or with reduce:
function fakeBin(x) {
x = x.split('');
let toBin = x => {
if (x < 5)
return 0;
else return 1
}
return x.map(toBin).join('');
}
console.log(fakeBin("2351"));
refactored
function fakeBin(x) {
x = [...x];
let toBin = x => x < 5 ? 0 : 1;
return x.map(toBin).join('');
}
console.log(fakeBin("2351"));
reduce
function fakeBin(x) {
let toBin = x => x < 5 ? 0 : 1;
return [...x].reduce((acc,val) => acc + toBin(val), "");
}
console.log(fakeBin("23519"));
You can use String.prototype.replace() with RegExp /([0-4])|([5-9])/g to match 0-4, 5-9, replace with 0, 1 respectively
let str = "8539734222673566";
let res = str.replace(/([0-4])|([5-9])/g, (_, p1, p2) => p1 ? 0 : 1);
console.log(res);

converting string to array of arrays and elements

I have a string containing numbers and different mathematical operators. How can I parse this string from var str = "123+45-34"; an convert it to an array
var arr = [123, '+', 45, '-',34];
One approach would be to split the string using a regex and then convert the parts into numbers where possible:
var str = "123+45-34";
var matches = str.match(/(\d+|\+|-|\/|\*)/g);
console.log(matches); // ["123", "+", "45", "-", "34"]
var asNumbers = matches.map(function(match) {
return +match || match
})
console.log(asNumbers); // [123, "+", 45, "-", 34]
It looks like you want to split your string on word boundaries:
var str = "123+45-34";
console.log(str.split(/\b/));
You could use a different approach with an operator object, which could be usefull for calculating the value later.
function split(s) {
var a = s.split(''),
i = 1;
while (i < a.length) {
if (!(a[i - 1] in operators || a[i] in operators)) {
a[i - 1] += a.splice(i, 1)[0];
continue;
}
i++;
}
return a.map(function (b) {
return b in operators ? b : +b;
});
}
var operators = { '+': true, '-': true },
str = "123+45-34+1e13";
console.log(split(str));
This code tries to traverse through the string.Each charecter is appended into a newstring,until + or - is found,once they are found string formed till now will be pushed into newarray and special charecter either + or - is also pushed into newarray and again this process continues till the end of the string
Ex:123+
it traverses the string.
first my newString ="1" ,then newString="12" finally newString="123"
as soon as + is found ,it pushes newString into newarray.now newArray is ["123" ] and '+' should also be pushed ,now array becomes ["123","+"].this process continues
Here i have taken into consideration of special charecters as only + and -
var str="123+45-34";
var newarr=[];
var newStr="";
for(var index=0;index<str.length;index++){
if(str[index]!='+'&& str[index]!='-')
newStr+=str[index];
else{
newarr.push(newStr);
newarr.push(str[index]);
newStr="";
}
}
console.log(newarr);
Hope this helps

Categories