How do I compare 2 letters in JavaScript to check if they are subsequent letters in the alphabet. Basically, the 2 letters have to be next to each other in the alphabet.
for example, comparing:
a to b would be true
a to c would be false
For my program I receive an input array such as ["a","b","e"] and I want to loop through this array and compare each item to the one before it, below is my code:
function fearNotLetter(str) {
str = str.split("");
console.log(str);
for(let i=1; i<str.length; i++){
console.log(str[i]>str[i-1]);
if(str[i] > str[i-1]){
console.log("good order");
} else {
console.log("missing");
}
}
return str;
}
so far I'm not sure what to do in the inner if statement I just tried comparing the items to check if item at index i is bigger than item at index i-1.
Extract both characters' charCodes, then check that they differ by 1, and are within the alphabetical range:
const checkAlphaAdjacent = (c1, c2) => {
const [code1, code2] = [c1.charCodeAt(), c2.charCodeAt()];
return Math.abs(code1 - code2) === 1 && [c1, c2].every(char => /[a-z]/i.test(char));
};
console.log(checkAlphaAdjacent('a', 'b'));
console.log(checkAlphaAdjacent('a', 'c'));
console.log(checkAlphaAdjacent('A', 'C'));
console.log(checkAlphaAdjacent('B', 'C'));
Try this:
const checkSequence = (c1, c2) => {
const char1 = c1.toLowerCase().charCodeAt();
const char2 = c2.toLowerCase().charCodeAt();
return Math.abs(char1 - char2) === 1;
};
console.log(checkSequence('a','B'));
console.log(checkSequence('a','b'));
console.log(checkSequence('B','a'));
console.log(checkSequence('B','B'));
in this solution if a comes after b answer is true. if you don't want this just change this line:
return Math.abs(char1 - char2) === 1;
to
char2 - char1 === 1;
so ['a','b'] is true and ['b','a'] is false.
in other approach if you want to ['b','b'] is ok so change ===1 to <=1 and so on.
Related
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));
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')
Thanks to Nina I have a code to compare two sentences word by word and return the number of word matches like this:
function includeWords(wanted, seen) {
var wantedMap = wanted.split(/\s+/).reduce((m, s) => m.set(s, (m.get(s) || 0) + 1), new Map),
wantedArray = Array.from(wantedMap.keys()),
count = 0;
seen.split(/\s+/)
.forEach(s => {
var key = wantedArray.find(t => s === t || s.length > 3 && t.length > 3 && (s.startsWith(t) || t.startsWith(s)));
if (!wantedMap.get(key)) return;
console.log(s, key)
++count;
wantedMap.set(key, wantedMap.get(key) - 1);
});
return count;
}
let matches = includeWords('i was sent to earth to protect you introduced', 'they\'re were protecting him i knew that i was aware introducing');
console.log('Matched words: ' + matches);
The code works fine, but there is still one issue:
What if we want to return a match for introduced and introducing too?
If you want the program to consider the words 'introduce' and 'introducing' as a match, it would amount to a "fuzzy" match (non binary logic). One simple way of doing this would require more code, the algorithm of which would possibly resemble
Take 2 words that you wish to match, tokenize into ordered list
of letters
Compare positionally the respective letters, i.e
match a[0]==b[0]? a[1]==b[1] where a[0] represents the first letter
of the first word and b[0] represents the first tokenized
letter/character potential match candidate
KEep a rolling numeric count of such positional matches. In this case it is 8 (introduc).
divide by word length of a = 8/9 call this f
divide by word length of b = 8/11 call this g
Provide a threshold value beyond which the program will consider it a match. eg. if you say anything above 70% in BOTH f and g can be
considered a match - viola, you have your answer!
Please note that there is some normalization also needed to prevent low length words from becoming false positives. you can add a constraint that the aforementioned calculation applies to words with at least 5 letters(or something to that effect!
Hope this helps!!
Regards,
SR
You could calculate similarites for a word pair and get a relation how many characters are similar bei respecting the length of the given word and the wanted pattern.
function getSimilarity(a, b) {
var i = 0;
while (i < a.length) {
if (a[i] !== b[i]) break;
i++;
}
return i / Math.max(a.length, b.length);
}
console.log(getSimilarity('abcdefghij', 'abc')); // 0.3
console.log(getSimilarity('abcdefghij', 'abcdef')); // 0.6
console.log(getSimilarity('abcdefghij', 'abcdefghij')); // 1
console.log(getSimilarity('abcdef', 'abcdefghij')); // 0.6
console.log(getSimilarity('abcdefghij', 'abcdef')); // 0.6
console.log(getSimilarity('abcdefghij', 'xyz')); // 0
console.log(getSimilarity('introduced', 'introducing')); // 0.7272727272727273
Here's a quick fix solution.
It's not intended as a complete solution.
Since the English language has more than a few quirks that would almost require an AI to understand the language.
First add a function that can compare 2 words and returns a boolean.
It'll also make it easier to test for specific words, and adapt to what's really needed.
For example, here's a function that does the simple checks that were already used.
Plus an '...ed' versus '...ing' check.
function compareWords (word1, word2) {
if (word1 === word2)
return true;
if (word1.length > 3 && word2.length > 3) {
if (word1.startsWith(word2) || word2.startsWith(word1))
return true;
if (word1.length > 4 && word2.length > 4) {
if (/(ing|ed)$/.test(word1) && word1.replace(/(ing|ed)$/, 'inged') === word2.replace(/(ing|ed)$/, 'inged'))
return true;
}
}
return false;
}
//
// tests
//
let words = [
["same", "same"],
["different", "unsame"],
["priced", "pricing"],
["price", "priced"],
["producing", "produced"],
["produced", "producing"]
];
words.forEach( (arr, idx) => {
let word1= arr[0];
let word2= arr[1];
let isSame = compareWords(word1, word2);
console.log(`[${word1}] ≈ [${word2}] : ${isSame}`);
});
Then use it in the code you already have.
...
seen.split(/\s+/)
.forEach(s => {
var key = wantedArray.find(t => compareWords(t, s));
...
Regarding string similarity, here's f.e. an older SO post that has some methods to compare strings : Compare Strings Javascript Return %of Likely
I have implemented this, it seems to work fine. any suggestions would be appreciated..
let speechResult = "i was sent to earth to introducing protect yourself introduced seen";
let expectSt = ['they were protecting him knew introducing that you i seen was aware seen introducing'];
// Create arrays of words from above sentences
let speechResultWords = speechResult.split(/\s+/);
let expectStWords = expectSt[0].split(/\s+/);
function includeWords(){
// Declare a variable to hold the count number of matches
let arr = [];
for(let a = 0; a < speechResultWords.length; a++){
for(let b = 0; b < expectStWords.length; b++){
if(similarity(speechResultWords[a], expectStWords[b]) > 69){
arr.push(speechResultWords[a]);
console.log(speechResultWords[a] + ' includes in ' + expectStWords[b]);
}
} // End of first for loop
} // End of second for loop
let uniq = [...new Set(arr)];
return uniq.length;
};
let result = includeWords();
console.log(result)
// The algorithmn
function similarity(s1, s2) {
var longer = s1;
var shorter = s2;
if (s1.length < s2.length) {
longer = s2;
shorter = s1;
}
var longerLength = longer.length;
if (longerLength == 0) {
return 1.0;
}
return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength)*100;
}
function editDistance(s1, s2) {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
var costs = new Array();
for (var i = 0; i <= s1.length; i++) {
var lastValue = i;
for (var j = 0; j <= s2.length; j++) {
if (i == 0)
costs[j] = j;
else {
if (j > 0) {
var newValue = costs[j - 1];
if (s1.charAt(i - 1) != s2.charAt(j - 1))
newValue = Math.min(Math.min(newValue, lastValue),
costs[j]) + 1;
costs[j - 1] = lastValue;
lastValue = newValue;
}
}
}
if (i > 0)
costs[s2.length] = lastValue;
}
return costs[s2.length];
}
I am trying to create a function compare() which when given an even letter of the alphabet returns the letter of index one below it and when given an odd letter of the alphabet returns the letter of index one above it (up to the letter 'e'). As such:
function compare(letter) {
var arr = {'a':'b', 'b':'a', 'c':'d', 'd':'c', 'e':'f', 'f':'e'}
return arr[letter]
}
The problem is that this array is going to be very long, and I feel like repeating each element in reverse is a bit of a waste. Is there a way of modifying the function so that I can store something like
arr={'a':'b', 'c':'d', 'e':'f'}
Instead of
arr={'a':'b', 'b':'a', 'c':'d', 'd':'c', 'e':'f', 'f':'e'}+
You don't need an object or array for this, you can use the char code to determine what the new code should be. Something like so
function getLetter(letter) {
var code = letter.charCodeAt(0);
var newCode = code % 2 == 0 ? code - 1 : code + 1;
return String.fromCharCode(newCode);
}
console.log(getLetter('a'));
console.log(getLetter('b'));
console.log(getLetter('c'));
console.log(getLetter('d'));
console.log(getLetter('e'));
console.log(getLetter('f'));
I think you are misunderstanding the array concept.
arr = {'a':'b', 'b':'a', 'c':'d', 'd':'c', 'e':'f', 'f':'e'} is not an array, but a Hash.
The correct way should be:
function compare(letter){
var arr = ['a','b','c','d','e','f']
var index = arr.indexOf(letter)
return index % 2 == 0 ? arr[index + 1] : arr[index - 1]
}
indexOf returns the position of the item you are checking. Then we check if it's odd or even and return the below/above corresponding letter.
Just check if it's even or odd, and return the letter above/below.
function compare(letter) {
var code = letter.charCodeAt(0);
if (code % 2) {
return String.fromCharCode(code + 1);
} else {
return String.fromCharCode(code - 1);
}
}
Example output:
compare('a')
"b"
compare('c')
"d"
compare('b')
"a"
compare('d')
"c"
const alphabet = ["a", "b", "c", "d", "e"];
const pairs = alphabet.reduce((acc, letter, index) => {
acc[letter] = alphabet[index+1] || alphabet[0];
return acc;
}, {});
console.log(pairs)
I want to identify strings that are made up exclusively of same-length character groups. Each one of these groups consists of at least two identical characters. So, here are some examples:
aabbcc true
abbccaa false
xxxrrrruuu false (too many r's)
xxxxxfffff true
aa true (shortest possible positive example)
aabbbbcc true // I added this later to clarify my intention
#ilkkachu: Thanks for your remark concerning the repetition of the same character group. I added the example above. Yes, I want the last sample to be tested as true: a string made up of the two letter groups aa, bb, bb, cc.
Is there a simple way to apply this condition-check on a string using regular expressions and JavaScript?
My first attempt was to do something like
var strarr=['aabbcc','abbccaa','xxxrrrruuu',
'xxxxxfffff','aa','negative'];
var rx=/^((.)\2+)+$/;
console.log(strarr.map(str=>str+': '+!!str.match(rx)).join('\n'));
It does look for groups of repeated characters but does not yet pay attention to these groups all being of the same length, as the output shows:
aabbcc: true
abbccaa: false
xxxrrrruuu: true // should be false!
xxxxxfffff: true
aa: true
aabbbbcc: true
negative: false
How do I get the check to look for same-length character groups?
To get all the groups of the same character has an easy regex solution:
/(.)\1*/g
Just repeating the backreference \1 of the character in capture group 1.
Then just check if there's a length in the array of same character strings that doesn't match up.
Example snippet:
function sameLengthCharGroups(str)
{
if(!str) return false;
let arr = str.match(/(.)\1*/g) //array with same character strings
.map(function(x){return x.length}); //array with lengths
let smallest_length = arr.reduce(function(x,y){return x < y ? x : y});
if(smallest_length === 1) return false;
return arr.some(function(n){return (n % smallest_length) !== 0}) == false;
}
console.log("-- Should be true :");
let arr = ['aabbcc','xxxxxfffff','aa'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should also be true :");
arr = ['aabbbbcc','224444','444422',
'666666224444666666','666666444422','999999999666666333'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should be false :");
arr = ['abbcc','xxxrrrruuu','a','ab','',undefined];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
ECMAScript 6 version with fat arrows (doesn't work in IE)
function sameLengthCharGroups(str)
{
if(!str) return false;
let arr = str.match(/(.)\1*/g)
.map((x) => x.length);
let smallest_length = arr.reduce((x,y) => x < y ? x : y);
if(smallest_length === 1) return false;
return arr.some((n) => (n % smallest_length) !== 0) == false;
}
Or using exec instead of match, which should be faster for huge strings.
Since it can exit the while loop as soon a different length is found.
But this has the disadvantage that this way it can't get the minimum length of ALL the lengths before comparing them.
So those with the minimum length at the end can't be found as OK this way.
function sameLengthCharGroups(str)
{
if(!str) return false;
const re = /(.)\1*/g;
let m, smallest_length;
while(m = re.exec(str)){
if(m.index === 0) {smallest_length = m[0].length}
if(smallest_length > m[0].length && smallest_length % m[0].length === 0){smallest_length = m[0].length}
if(m[0].length === 1 ||
// m[0].length !== smallest_length
(m[0].length % smallest_length) !== 0
) return false;
}
return true;
}
console.log("-- Should be true :");
let arr = ['aabbcc','xxxxxfffff','aa'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should also be true :");
arr = ['aabbbbcc','224444','444422',
'666666224444666666','666666444422','999999999666666333'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should be false :");
arr = ['abbcc','xxxrrrruuu','a','ab','',undefined];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
Here's one that runs in linear time:
function test(str) {
if (str.length === 0) return true;
let lastChar = str.charAt(0);
let seqLength = 1;
let lastSeqLength = null;
for (let i = 1; i < str.length; i++) {
if (str.charAt(i) === lastChar) {
seqLength++;
}
else if (lastSeqLength === null || seqLength === lastSeqLength) {
lastSeqLength = seqLength;
seqLength = 1;
lastChar = str.charAt(i);
}
else {
return false;
}
}
return (lastSeqLength === null || lastSeqLength === seqLength);
}
Since requirements changed or weren't clear as now this is the third solution I'm posting. To accept strings that could be divided into smaller groups like aabbbb we could:
Find all lengths of all different characters which are 2 and 4 in this case.
Push them into an array named d.
Find the lowest length in set named m.
Check if all values in d have no remainder when divided by m
Demo
var words = ['aabbbcccdddd', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aab', 'aabbbbccc'];
words.forEach(w => {
var d = [], m = Number.MAX_SAFE_INTEGER;
var s = w.replace(/(.)\1+/gy, x => {
d.push(l = x.length);
if (l < m) m = l;
return '';
});
console.log(w + " => " + (s == '' && !d.some(n => n % m != 0)));
});
Using sticky flag y and replace method you could do this much more faster. This trick replaces occurrences of first one's length with an empty string (and stops as soon as an occurrence with different length happens) then checks if there are some characters left:
var words = ['aabbcc', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aa'];
words.forEach(w => {
console.log(w + " => " + (w.replace(/(.)\1+/gy, ($0, $1, o) => {
return $0.length == (o == 0 ? l = $0.length : l) ? '' : $0;
}).length < 1));
});
Another workaround would be using replace() along with test(). First one replaces different characters with their corresponding length and the second looks for same repeated numbers in preceding string:
var str = 'aabbc';
/^(\d+\n)\1*$/.test(str.replace(/(.)\1+/gy, x => x.length + '\n'));
Demo:
var words = ['aabbcc', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aa'];
words.forEach(w =>
console.log(/^(\d+\n)\1*$/.test(w.replace(/(.)\1+/gy, x => x.length + '\n')))
);
Since regex has never been my forte here's an approach using String#replace() to add delimiter to string at change of letter and then use that to split into array and check that all elements in array have same length
const values = ['aabbcc', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aa'];
const expect = [true, false, false, true, true];
const hasMatchingGroups = (str) => {
if(!str || str.length %2) return false;
const groups = str.replace(/[a-z]/g,(match, offset, string) => {
return string[offset + 1] && match !== string[offset + 1] ? match + '|' : match;
}).split('|');
return groups.every(s => s.length === groups[0].length)
}
values.forEach((s, i) => console.log(JSON.stringify([s,hasMatchingGroups(s), expect[i]])))
The length of the repeated pattern of same charcters needs to be specified within the regular expression. The following snippet creates regular expressions looking for string lengths of 11 down to 2. The for-loop is exited once a match is found and the function returns the length of the pattern found:
function pat1(s){
for (var i=10;i;i--)
if(RegExp('^((.)\\2{'+i+'})+$').exec(s))
return i+1;
return false;}
If nothing is found false is returned.
If the length of the pattern is not required, the regular expression can also be set up in one go (without the need of the for loop around it):
function pat2(s){
var rx=/^((.)\2)+$|^((.)\4{2})+$|^((.)\6{4})+$|^((.)\8{6})+$/;
return !!rx.exec(s);
}
Here are the results from both tests:
console.log(strarr.map(str=>
str+': '+pat1(str)
+' '+pat2(str)).join('\n')+'\n');
aabbcc: 2 true
abbccaa: false false
xxxrrrruuu: false false
xxxxxfffff: 5 true
aa: 2 true
aabbbbcc: 2 true
negative: false false
The regex in pat2 looks for certain repetition-counts only. When 1, 2, 4 or 6 repetitions of a previous character are found then the result is positive. The found patterns have lengths of 2,3,5 or 7 characters (prime numbers!). With these length-checks any pattern-length dividable by one of these numbers will be found as positive (2,3,4,5,6,7,8,9,10,12,14,15,16,18,20,21,22,24,...).