What's wrong with this Codility Answer? - javascript

The question was to find the largest binary gap in a number, and while this worked in my IDE, Codility didn't accept it. Any thoughts?
const biGap = (number) => {
const binary = number.toString(2)
const re = /1(0+)1/g;
const found = binary.match(re)
let counter = 0
found.map((match) => {
if (match.length > counter) {
counter = match.length
}
})
return (counter - 2);
}
console.log(biGap(1041));

The main problem with your code is that binary.match(re) won't return overlapping matches. So if binary = "1010000001001", it will return ["101", "1001"], which is missing the long gap 10000001 between them.
You can solve this by changing the regexp to
const re = /0+1/g;
Then you should return counter - 1 instead of counter - 2.
You don't need to put 1 on both sides of the 0+ because number.toString(2) will never include leading zeroes, so there's always a 1 to the left of any string of zeroes, and it's not necessary to match it explicitly.
If you also want to include the binary gap in the low order bits of the number, you can change the regexp to simply:
const re = /0+/g;
Then you don't need to subtract anything from counter when returning.
const biGap = (number) => {
const binary = number.toString(2)
const re = /0+1/g;
const found = binary.match(re)
let counter = 0
found.map((match) => {
if (match.length > counter) {
counter = match.length
}
})
return (counter - 1);
}
console.log(biGap(1041));
console.log(biGap(parseInt("1010000001001", 2))); // Your code returns 2

function binGap(N) {
var max=0;
var count=0;
var binary = Number(N).toString(2);;
Array.prototype.forEach.call(binary,function(i) {
if(i == 0) {
count++;
} else {
if(count > max) {
max = count;
}
count = 0;
}
});
return max;
}

for starters make sure that the regex returns all the right candidates
map operator is used the wrong way. Reduce is the way to use in your case
you should not subtract 2 from the counter at return, instead do that in the reduce callback
don't console.log
And a final thought, why convert the number into string? why not use modulo 2? it's way simpler and efficient. (think of how much resources a regex requires)

a possible solution may be this
solution(N) {
while(N && N%2 === 0) {
N = N>>1
}
let counter = 0;
let tempCounter = 0;
let n=N;
while(n) {
if(n%2 === 1) {
counter = Math.max(counter, tempCounter);
tempCounter=0;
} else {
tempCounter = tempCounter + 1;
}
n = n>>1;
}
return counter;
}

Related

Javascript find the most repetitive character occurrence from the string

Let's say we have this string:
BBBBAAAABBAAAAAACCCCCBDDDDEEEEEEE,FFF
As you can see, here B is occurring 4 times at first but B is also present before DDDD.
Similarly, A is occurring 4 times at the beginning and later 6 times.
I want the expected output if I am searching B it should 4 times as the max occurrence B is 4. However if I am searching A then it should return 6 because the most occurrence for A is 6.
Here is my code I tried:
function checkRepeatativeString(str) {
let hashMap = {};
let seen = new Set();
let counter = 1;
let maxValue = 1;
let isPreviousValueSame = false;
let isNextValueSame = true;
for (let i = 0; i < str.length; i++) {
/**
* is previous value same
*/
if (str[i] == str[i-1]) {
isPreviousValueSame = true;
}
/**
* is next value same
*/
if (str[i] == str[i+1]) {
isNextValueSame = true;
}
if (seen.has(str[i]) && isPreviousValueSame) {
hashMap[str[i]][0]++;
hashMap[str[i]][1]++;
isPreviousValueSame = false;
} else if(seen.has(str[i]) && !isNextValueSame) {
maxValue = Math.max(hashMap[str[i]][1], maxValue);
counter = 0;
hashMap[str[i]] = [counter, maxValue];
} else {
maxValue = Math.max(maxValue, counter);
seen.add(str[i]);
hashMap[str[i]] = [counter, maxValue];
isPreviousValueSame = false;
}
}
return hashMap;
}
let str = "BBBBAAAABBAAAAAACCCCCBDDDDEEEEEEE,FFF";
console.log(checkRepeatativeString(str));
This code is working but if you look for B, I am getting stuck at the beginning of value.
My program returns out for B:
B: [ 1, 1 ]
^ ^
Inside array, 1 is a counter which scans the string and second 1 in array is a max value which should return the output. However my program is returning 1 for B. I am expecting 4 as max value.
Help would be appreciated~
Quick and dirty.
function maxConsecutiveCharacters(check, haystack) {
if(check.length !== 1) return false;
let result = 0;
let buffer = 0;
for(let i = 0; i < haystack.length; i++) {
if(haystack[i] === check) {
buffer++;
}
else {
if(buffer > result) {
result = buffer;
}
buffer = 0;
}
if(buffer > result) {
result = buffer;
}
}
return result;
}
That looks overly complicated. Consider approaching the problem from a different angle - first split up the string into segments of repeating characters, and group them into an object based on the length of the longest substring for a given character.
const checkRepeatativeString = (str) => {
const longestCounts = {};
for (const consecutive of (str.match(/(.)\1*/g) || [])) {
const char = consecutive[0];
longestCounts[char] = Math.max(
longestCounts[char] || 0, // Use the existing value in the object if it exists and is higher
consecutive.length // Otherwise, use the length of the string iterated over
);
}
return longestCounts;
};
let str = "BBBBAAAABBAAAAAACCCCCBDDDDEEEEEEE,FFF";
console.log(checkRepeatativeString(str));
Simpler code often means less surface area for bugs.

Concept of a static variable inside a function when called by string.replace

I have the following code to do a basic IP validation. Actually, it's just academic and I'm using this code to practice using String.replace (intentionally using a poor/loose regex):
let potential_ips = [
'1.2.3.4.5',
'12.34.45.56',
'123.456.789.0',
'1.2.3.4.5.6'
]
var group_num = 0;
function rePlace(m, g1) {
group_num ++;
if (parseInt(g1) > 255) {
return '<invalid>'
} else if (group_num > 4) {
return '<invalid>'
} else {
return m;
}
}
const regex = /(\d{1,3})\.?/g;
for (ip of potential_ips) {
group_num = 0;
ip = ip.replace(regex, rePlace);
console.log('222', ip);
}
What I'm trying to do is when the .replace function is called to keep a running total of the number of times it's been called for that particular string. Currently I'm using a var at the top-level scope, but I'm wondering if there is a sort of cleaner way to do this within the function itself? What would be a better approach to doing this?
You could take a closure over the count and initialize with zero.
const
rePlace = count => m => {
count++;
if (parseInt(m) > 255 || count > 4) return '<invalid>';
return m;
},
regex = /\d+(?=(\.|$))/g,
potential_ips = ['1.2.3.4.5', '12.34.45.56', '123.456.789.0', '1.2.3.4.5.6'];
for (let ip of potential_ips) {
ip = ip.replace(regex, rePlace(0));
console.log('222', ip);
}
The concept here is copied from Nina's answer, but here is one approach where the rePlace function now returns a bound function with an initialized variable to zero (perhaps a bit like Church numerals?)
function rePlace(count) {
return function(m) {
count ++;
return (parseInt(m) > 255 || count > 4) ? '<invalid>' : m;
}
}
const regex = /\d+(?=(\.|$))/g
const potential_ips = ['1.2.3.4.5', '12.34.45.56', '123.456.789.0', '1.2.3.4.5.6'] ;
for (let ip of potential_ips) {
ip = ip.replace(regex, rePlace(0));
console.log('222', ip);
}

Find the minium paranthese to be added at start or end of a parantheses string to make it balance

Need to balance a parentheses by adding minimum required parentheses at the start or end.
Example-
If I = "(()(())" then
R = [0,1]
I created a solution like
function bPar(s){
let stack1 = [];
let result = [0,0];
s.split("").forEach(x=>{
if(x==="("){
stack1.push("(");
}else if(x===")"){
if(stack1[stack1.length-1]==="("){
stack1.pop();
}else{
stack1.push(")")
}
}
})
stack1.forEach(x=>{
if(x=="("){
result[1]=result[1]+1;
}else if(x==")"){
result[0]=result[0]+1;
}
})
return result;
}
console.log(bPar("(()(())"));
But I think the time complexity is higher. Is their any better approach?
Instead of having a stack array, you could keep track of the number of currently open parentheses. If you come across a ) while that number is 0, add a number to the number of additional required (s:
function bPar(s){
let additionalOpensNeededAtBegin = 0;
let openCount = 0;
for (const char of s) {
if (char === '(') openCount++;
else {
if (openCount === 0) additionalOpensNeededAtBegin++;
else openCount--;
}
}
return [additionalOpensNeededAtBegin, openCount];
}
console.log(bPar("(()(())"));
console.log(bPar("((((("));
console.log(bPar(")))))"));
console.log(bPar(")(((("));
console.log(bPar("))))("));
console.log(bPar("(())"));

Return String as Sorted Blocks - Codewars Challenge - JavaScript

I've been trying to solve this codewars challenge. The idea is to return the string, rearranged according to its hierarchy, or separated into chunks according to the repeating character.
You will receive a string consisting of lowercase letters, uppercase letters and digits as input. Your task is to return this string as blocks separated by dashes ("-"). The elements of a block should be sorted with respect to the hierarchy listed below, and each block cannot contain multiple instances of the same character.
The hierarchy is:
lowercase letters (a - z), in alphabetic order
uppercase letters (A - Z), in alphabetic order
digits (0 - 9), in ascending order
Examples
"21AxBz" -> "xzAB12"
since input does not contain repeating characters, you only need 1 block
"abacad" -> "abcd-a-a"
character "a" repeats 3 times, thus 3 blocks are needed
"" -> ""
an empty input should result in an empty output
What I've tried actually works for the given test cases:
describe("Sample tests", () => {
it("Tests", () => {
assert.equal(blocks("21AxBz"), "xzAB12");
assert.equal(blocks("abacad"), "abcd-a-a");
assert.equal(blocks(""), "");
});
});
But fails when there are any repeating characters, besides in the test cases:
function repeatingChar(str){
const result = [];
const strArr = str.toLowerCase().split("").sort().join("").match(/(.)\1+/g);
if (strArr != null) {
strArr.forEach((elem) => {
result.push(elem[0]);
});
}
return result;
}
function blocks(s) {
if (s.length === 0) {
return '';
}
//if string does not contain repeating characters
if (!/(.).*\1/.test(s) === true) {
let lower = s.match(/[a-z]/g).join('');
let upper = s.match(/[A-Z]/g).join('');
let numbers = s.match(/[\d]/g).sort().join('');
return lower + upper + numbers;
}
//if string contains repeating characters
if (!/(.).*\1/.test(s) === false) {
let repeatChar = (repeatingChar(s)[0]);
let repeatRegex = new RegExp(repeatingChar(s)[0]);
let repeatCount = s.match(/[repeatRegex]/gi).length;
let nonAChars = s.match(/[^a]/gi).join('');
function getPosition(string, subString, index) {
return s.split(repeatChar, index).join(repeatChar).length;
}
let index = getPosition(s, repeatChar, 2);
// console.log('indexxxxx', index);
return s.slice(0, index) + nonAChars.slice(1) + ('-' + repeatChar).repeat(repeatCount - 1);
}
}
console.log(blocks("abacad"));
And actually, I'm not sure what's wrong with it, because I don't know how to unlock any other tests on Codewars.
You can see that what I'm trying to do, is find the repeating character, get all characters that are not the repeating character, and slice the string from starting point up until the 2 instance of the repeating character, and then add on the remaining repeating characters at the end, separated by dashes.
Any other suggestions for how to do this?
For funzies, here's how I would have approached the problem:
const isLower = new RegExp('[a-z]');
const isUpper = new RegExp('[A-Z]');
const isDigit = new RegExp('[0-9]');
const isDigitOrUpper = new RegExp('[0-9A-Z]');
const isDigitOrLower = new RegExp('[0-9a-z]');
const isLowerOrUpper = new RegExp('[a-zA-Z]');
function lowerUpperNumber(a, b)
{
if(isLower.test(a) && isDigitOrUpper.test(b))
{
return -1;
}
else if(isUpper.test(a) && isDigitOrLower.test(b))
{
if(isDigit.test(b))
{
return -1;
}
else if(isLower.test(b))
{
return 1;
}
}
else if(isDigit.test(a) && isLowerOrUpper.test(b))
{
return 1;
}
else if(a > b)
{
return 1;
}
else if(a < b)
{
return -1;
}
return 0;
}
function makeBlocks(input)
{
let sortedInput = input.split('');
sortedInput.sort(lowerUpperNumber);
let output = '';
let blocks = [];
for(let c of sortedInput)
{
let inserted = false;
for(let block of blocks)
{
if(block.indexOf(c) === -1)
{
inserted = true;
block.push(c);
break;
}
}
if(!inserted)
{
blocks.push([c]);
}
}
output = blocks.map(block => block.join('')).join('-');
return output;
}
console.log(makeBlocks('21AxBz'));
console.log(makeBlocks('abacad'));
console.log(makeBlocks('Any9Old4String22With7Numbers'));
console.log(makeBlocks(''));
The first obvious error I can see is let repeatCount = s.match(/[repeatRegex]/gi).length;. What you really want to do is:
let repeatRegex = new RegExp(repeatingChar(s)[0], 'g');
let repeatCount = s.match(repeatRegex).length;
The next is that you only look at one of the repeating characters, and not all of them, so you won't get blocks of the correct form, so you'll need to loop over them.
let repeatedChars = repeatingChar(s);
for(let c of repeatedChars)
{
//figure out blocks
}
When you're building the block, you've decided to focus on everything that's not "a". I'm guessing that's not what you originally wrote, but some debugging code, to work on that one sample input.
If I understand your desire correctly, you want to take all the non-repeating characters and smoosh them together, then take the first instance of the first repeating character and stuff that on the front and then cram the remaining instances of the repeating character on the back, separated by -.
The problem here is that the first repeating character might not be the one that should be first in the result. Essentially you got lucky with the repeating character being a.
Fixing up your code, I would create an array and build the blocks up individually, then join them all together at the end.
let repeatedChars = repeatingChar(s);
let blocks = []
for(let c of repeatedChars)
{
let repeatRegex = new RegExp(c, 'g');
let repeatCount = s.match(repeatRegex).length;
for(let i = 1; i <= repeatCount; i++)
{
if(blocks.length < i)
{
let newBlock = [c];
blocks.push(newBlock);
}
else
{
block[i - 1].push(c);
}
}
}
let tailBlocks = blocks.map(block => block.join('')).join('-');
However, this leaves me with a problem of how to build the final string with the non-repeating characters included, all in the right order.
So, to start with, let's make the initial string. To do so we'll need a custom sort function (sorry, it's pretty verbose. If only we could use regular ASCII ordering):
function lowerUpperNumber(a, b)
{
if(a.match(/[a-z]/) && b.match(/[A-Z0-9]/))
{
return -1;
}
else if(a.match(/[A-Z]/) && (b.match(/[0-9]/) || b.match(/[a-z]/)))
{
if(b.match(/[0-9]/))
{
return -1;
}
else if(b.match(/[a-z]/))
{
return 1;
}
}
else if(a.match(/[0-9]/) && b.match(/[a-zA-Z]/))
{
return 1;
}
else if(a > b)
{
return 1;
}
else if(a < b)
{
return -1;
}
return 0;
}
Then create the head of the final output:
let firstBlock = [...(new Set(s))].sort(lowerUpperNumber);
The Set creates a set of unique elements, i.e. no repeats.
Because we've created the head string, when creating the blocks of repeated characters, we'll need one fewer than the above loop gives us, so we'll be using s.match(repeatRegex).length-1.
I get the desire to short circuit the complicated bit and return quickly when there are no repeated characters, but I'm going to remove that bit for brevity, and also I don't want to deal with undefined values (for example try '123' as your input).
Let's put it all together:
function lowerUpperNumber(a, b)
{
if(a.match(/[a-z]/) && b.match(/[A-Z0-9]/))
{
return -1;
}
else if(a.match(/[A-Z]/) && (b.match(/[0-9]/) || b.match(/[a-z]/)))
{
if(b.match(/[0-9]/))
{
return -1;
}
else if(b.match(/[a-z]/))
{
return 1;
}
}
else if(a.match(/[0-9]/) && b.match(/[a-zA-Z]/))
{
return 1;
}
else if(a > b)
{
return 1;
}
else if(a < b)
{
return -1;
}
return 0;
}
function makeBlocks(s)
{
if (s.length === 0)
{
return '';
}
let firstBlock = [...(new Set(s))].sort(lowerUpperNumber);
let firstString = firstBlock.join('');
let blocks = [];
for(let c of firstString)
{
let repeatRegex = new RegExp(c, 'g');
let repeatCount = s.match(repeatRegex).length - 1;
for(let i = 1; i <= repeatCount; i++)
{
if(blocks.length < i)
{
let newBlock = [c];
blocks.push(newBlock);
}
else
{
blocks[i - 1].push(c);
}
}
}
blocks.unshift(firstBlock);
return blocks.map(block => block.join('')).join('-');
}
console.log(makeBlocks('21AxBz'));
console.log(makeBlocks('abacad'));
console.log(makeBlocks('Any9Old4String22With7Numbers'));
console.log(makeBlocks(''));
You'll see I've not bothered generating the characters that repeat, because I can just skip the ones that don't.

Code Wars Persistent Bugger issue with if-statement not running for-loop

https://www.codewars.com/kata/persistent-bugger/train/javascript
I'm working on the JavaScript Code Wars Persistent Bugger problem. As seen in the code, my if-statement appears to completely ignore the for-loop within it.
Anything logged within the for-loop does not show, and anything logged outside the for-loop remained the same as if the for-loop wasn't there. I've tried researching the use of for-loops inside if-statements as well as existing solutions for this Code Wars problem, but did not see my issue being replicated. From my understanding, the if-statement should run through everything within it from top to bottom.
const persistence = (num) => {
if (typeof(num) === 'number') {
let count = 0
let mult = num
let newMult = 1
let stop = false
while (stop === false){
if (mult >= 10) {
for (let i = 0; i < mult.length; i++) {
newMult = newMult * mult[i]
// // doesn't log anything. code isn't running through for loop?
// console.log(newMult)
}
// // logs initial num of 999
// console.log(mult)
// // logs initial newMult of 1
// console.log(newMult)
count++
mult = newMult
newMult = 1
}
else {
stop = true
}
}
return count
}
}
// logs 1, should be 4
console.log(persistence(999))
I think I have the logic correct, but maybe there's some fundamental issue I'm missing in regards to how if-statements and for-loops work. I noticed there are much simpler ways to solve this problem, but if I could receive feedback on why my specific code doesn't work, and how I could tweak it to make it work, that would be greatly appreciated.
You are iterating to the length of num on the for loop. As num is type number ,it has no length.
You need to iterate over a string version of mult (let's call it multStr) in order to access its numeric-indexed properties as digits. (Otherwise, it won't have a length, and accessing its numeric properties wouldn't give anything in return.)
You can also significantly clean up the code by returning early instead of having such nested blocks:
const persistence = (num) => {
if (typeof num !== 'number') {
return;
}
let count = 0
let mult = num
while (true) {
if (mult < 10) {
return count;
}
const multStr = String(mult); // <--------------------------
let newMult = 1
for (let i = 0; i < multStr.length; i++) { // <--------------------------
newMult = newMult * multStr[i] // <--------------------------
}
mult = newMult;
count++
}
}
console.log(persistence(999))
You could also consider using reduce to calculate the newMult instead:
const persistence = (num) => {
if (typeof num !== 'number') {
return;
}
let count = 0
let mult = num
while (true) {
if (mult < 10) {
return count;
}
mult = [...String(mult)].reduce((a, b) => a * b, 1);
count++
}
}
console.log(persistence(999))

Categories