Capture non-adjacent repeating letters - javascript

How do I capture repeating letters in a word like abababa = 2matches( a and b is repeating )
I know how to do it when the letters are adjacent like so /(\w)\1+/ .
Thanks

Try to use this String expansion:
String.prototype.getRepeating = function() {
var length = this.length;
var found = '';
var repeating = '';
var index;
var letter;
for (index = 0; index < length; index++) {
letter = this.charAt(index);
if (-1 == found.indexOf(letter)) {
found = found.concat(letter);
} else {
if (-1 == repeating.indexOf(letter)) {
repeating = repeating.concat(letter);
}
}
}
return repeating;
}
The tests:
var tests = ['ab', 'aa', 'bb', 'abab', 'abb', 'aab', 'bab'];
for (var index in tests) {
console.log(tests[index], '=>', tests[index].getRepeating());
}
ab => (an empty string)
aa => a
bb => b
abab => ab
abb => b
aab => a
bab => b

If I understand correctly, you want to extract letters which appear more than once in a given word. If so, you simply need to iterate over the letters of the word, accumulate their occurrence, then filter out letters which only appear once.
var testString = "abababa";
var letters = countGroupByLetter(testString);
var result = filterMap(letters, function(v) {
return v > 1;
});
console.log(result);
function countGroupByLetter(testString) {
var result = {};
for (var ii = 0; ii < testString.length; ii++) {
var letter = testString.charAt(ii);
if (result[letter]) {
result[letter] ++;
} else {
result[letter] = 1;
}
}
return result;
}
function filterMap(map, filterFunction) {
var result = {};
for (var p in map) {
if (filterFunction(map[p])) {
result[p] = map[p];
}
}
return result;
}
Since you already know about back references, I suppose that you know you can find out if there is a letter repetition in a string, using /(\w).*\1/. Capturing all repetitions in one pass would not be possible though, you'd still need to execute a pattern repeatedly and accumulate the matched characters (for instance using /(\w)(?=.*\1)/g). That, however, would not be optimal.
var repeatingLetters = /(\w)(?=.*\1)/g;
var testString = "abababa";
var captures = null;
var result = {};
while ((captures = repeatingLetters.exec(testString)) != null) {
result[captures[1]] = true;
}
console.log(result);

Related

JavaScript How to Create a Function that returns a string with number of times a characters shows up in a string

I am trying to figure out how to make a function that takes a string. Then it needs to return a string with each letter that appears in the function along with the number of times it appears in the string. For instance "eggs" should return e1g2s1.
function charRepString(word) {
var array = [];
var strCount = '';
var countArr = [];
// Need an Array with all the characters that appear in the String
for (var i = 0; i < word.length; i++) {
if (array.indexOf(word[i]) === false) {
array.push(word[i]);
}
}
// Need to iterate through the word and compare it with each char in the Array with characters and save the count of each char.
for (var j = 0; j < word.length; i++) {
for (var k = 0; k < array.length; k++){
var count = 0;
if (word[i] === array[k]){
count++;
}
countArr.push(count);
}
// Then I need to put the arrays into a string with each character before the number of times its repeated.
return strCount;
}
console.log(charRepString("taco")); //t1a1co1
console.log(charRepString("egg")); //e1g2
let str = prompt('type a string ') || 'taco'
function getcount(str) {
str = str.split('')
let obj = {}
for (i in str) {
let char = str[i]
let keys = Object.getOwnPropertyNames(obj)
if (keys.includes(char)) {
obj[char] += 1
} else {
obj[char] = 1
}
}
let result = ''
Object.getOwnPropertyNames(obj).forEach((prop) => {
result += prop + obj[prop]
})
return result
}
console.log(getcount(str))
If the order of the alphanumeric symbols matters
const str = "10zza";
const counted = [...[...str].reduce((m, s) => (
m.set(s, (m.get(s) || 0) + 1), m
), new Map())].flat().join("");
console.log(counted); // "1101z2a1"
Or also like (as suggested by Bravo):
const str = "10zza";
const counted = [...new Set([...str])].map((s) =>
`${s}${str.split(s).length-1}`
).join("");
console.log(counted); // "1101z2a1"
A more clear and verbose solution-
Let m be max number of symbols in charset
Time complexity- O(n log(m))
Space complexity- O(m)
function countFrequencies(str) {
const freqs = new Map()
for (const char of str) {
const prevFreq = freqs.get(char) || 0
freqs.set(char, prevFreq + 1)
}
return freqs
}
function getCountStr(str) {
const freqs = countFrequencies(str)
const isListed = new Set()
const resultArray = []
for (const char of str) {
if (isListed.has(char)) continue
resultArray.push(char)
resultArray.push(freqs.get(char))
isListed.add(char)
}
return resultArray.join("")
}
console.log(getCountStr("egg"))
console.log(getCountStr("taco"))
console.log(getCountStr("10za"))
Using Set constructor, first we will get the unique data.
function myfun(str){
let createSet = new Set(str);
let newArr = [...createSet].map(function(elem){
return `${elem}${str.split(elem).length-1}`
});
let newStr = newArr.join('');
console.log(newStr);
}
myfun('array');

Common Character Count in Strings JavaScript

Here is the problem:
Given two strings, find the number of common characters between them.
For s1 = "aabcc" and s2 = "adcaa", the output should be 3.
I have written this code :
function commonCharacterCount(s1, s2) {
var count = 0;
var str = "";
for (var i = 0; i < s1.length; i++) {
if (s2.indexOf(s1[i]) > -1 && str.indexOf(s1[i]) == -1) {
count++;
str.concat(s1[i])
}
}
return count;
}
console.log(commonCharacterCount("aabcc", "adcaa"));
It doesn't give the right answer, I wanna know where I am wrong?
There are other more efficient answers, but this answer is easier to understand. This loops through the first string, and checks if the second string contains that value. If it does, count increases and that element from s2 is removed to prevent duplicates.
function commonCharacterCount(s1, s2) {
var count = 0;
s1 = Array.from(s1);
s2 = Array.from(s2);
s1.forEach(e => {
if (s2.includes(e)) {
count++;
s2.splice(s2.indexOf(e), 1);
}
});
return count;
}
console.log(commonCharacterCount("aabcc", "adcaa"));
You can do that in following steps:
Create a function that return an object. With keys as letters and count as values
Get that count object of your both strings in the main function
Iterate through any of the object using for..in
Check other object have the key of first object.
If it have add the least one to count using Math.min()
let s1 = "aabcc"
let s2 = "adcaa"
function countChars(arr){
let obj = {};
arr.forEach(i => obj[i] ? obj[i]++ : obj[i] = 1);
return obj;
}
function common([...s1],[...s2]){
s1 = countChars(s1);
s2 = countChars(s2);
let count = 0;
for(let key in s1){
if(s2[key]) count += Math.min(s1[key],s2[key]);
}
return count
}
console.log(common(s1,s2))
After posting the question, i found that i havent looked the example well. i thought it wants unique common characters ..
and i changed it and now its right
function commonCharacterCount(s1, s2) {
var count = 0;
var str="";
for(var i=0; i<s1.length ; i++){
if(s2.indexOf(s1[i])>-1){
count++;
s2=s2.replace(s1[i],'');
}
}
return count;
}
Create 2 objects containing characters and their count for strings s1
and s2
Count the common keys in 2 objects and return count - Sum the common keys with minimum count in two strings
O(n) - time and O(n) - space complexities
function commonCharacterCount(s1, s2) {
let obj1 = {}
let obj2 = {}
for(let char of s1){
if(!obj1[char]) {
obj1[char] = 1
} else
obj1[char]++
}
for(let char of s2){
if(!obj2[char]) {
obj2[char] = 1
} else
obj2[char]++
}
console.log(obj1,obj2)
let count = 0
for(let key in obj1 ){
if(obj2[key])
count += Math.min(obj1[key],obj2[key])
}
return count
}
I think it would be a easier way to understand. :)
function commonCharacterCount(s1: string, s2: string): number {
let vs1 = [];
let vs2 = [];
let counter = 0;
vs1 = Array.from(s1);
vs2 = Array.from(s2);
vs1.sort();
vs2.sort();
let match_char = [];
for(let i = 0; i < vs1.length; i++){
for(let j = 0; j < vs2.length; j++){
if(vs1[i] == vs2[j]){
match_char.push(vs1[i]);
vs2.splice(j, 1);
break;
}
}
}
return match_char.length;
}
JavaScript ES6 clean solution. Use for...of loop and includes method.
var commonCharacterCount = (s1, s2) => {
const result = [];
const reference = [...s1];
let str = s2;
for (const letter of reference) {
if (str.includes(letter)) {
result.push(letter);
str = str.replace(letter, '');
}
}
// ['a', 'a', 'c'];
return result.length;
};
// Test:
console.log(commonCharacterCount('aabcc', 'adcaa'));
console.log(commonCharacterCount('abcd', 'aad'));
console.log(commonCharacterCount('geeksforgeeks', 'platformforgeeks'));
Cause .concat does not mutate the string called on, but it returns a new one, do:
str = str.concat(s1[i]);
or just
str += s1[i];
You can store the frequencies of each of the characters and go over this map (char->frequency) and find the common ones.
function common(a, b) {
const m1 = {};
const m2 = {};
let count = 0;
for (const c of a) m1[c] = m1[c] ? m1[c]+1 : 1;
for (const c of b) m2[c] = m2[c] ? m2[c]+1 : 1;
for (const c of Object.keys(m1)) if (m2[c]) count += Math.min(m1[c], m2[c]);
return count;
}

Get an array of matches in string as indexes

Example: "abc abc ab a".indexOfList("abc") returns [0,4]
My code:
String.prototype.indexOfList=function(word){
var l=[];
for(var i=0;i<this.length;i++){ //For each character
var pushed=(this[i]==word[0])&&(word.length+i<=this.length);
if (pushed) {
for(var j=1;j<word.length;j++){
if (word[j]!=this[i+j]) {
pushed=false; break;
}
}
}
if (pushed) {
l.push(i);
}
}
return l;
}
Is there a better and smaller way than this?
You can use the regex match command:
var re = /abc/g,
str = "abc abc ab a";
var pos = [];
while ((match = re.exec(str)) != null) {
pos.push(match.index);
}
There's a version that could handle overlapping strings, i.e. pattern aaa for a string aaaaa should return [0,1,2].
function indexOfList(needle, haystack) {
const result = [];
let i = 0;
while (haystack.includes(needle, i)) {
const match = haystack.indexOf(needle, i);
result.push(match);
i = match + 1;
}
return result;
}
indexOfList("abc", "abc abc ab a"), // [0, 4]
indexOfList("aaa", "aaaabc abc ab a") // [0, 1]
I would also advice against extending the prototype of a native object. It could lead to a very nasty name clashes.
Consider your collegue (or even a language maintainer) adds a function with the same name.
With indexOf function
var str = "abc abc ab a";
var i = -1;
var result = [];
while (true) {
i = str.indexOf("abc", i + 1);
if (i == -1) break;
result.push(i);
}
document.write(result);
No need to complicate things. A very similar method to this already exists: String.indexOf.
You can also pass a second parameter to this method, telling it where to start looking. If you keep increasing this second parameter, you can quickly find each occurrence.
String.prototype.indexOfList = function(word) {
var start = this.indexOf(word);
var l = [start]
if(start == -1) {
return [-1, -1];
}
var index = start;
for(var i = start + word.length; i < this.length - word.length; i = index) {
index = this.indexOf(word, i);
if(index == -1) {
break;
}
l.push(index);
}
return l;
}
This will start at the first occurrence, and continue to add on each index the word appears at.
you can use replace
String.prototype.indexOfList = function(word){
var l=[];
this.replace(new RegExp(word,"g"), (a,i) => l.push(i));
return l;
}
console.log("abc abc ab a".indexOfList("abc"));

Returning multiple index values from an array using Javascript

I have an array containing the individual letters of a word and i want to search the array to return the index values of certain letters. However, if the word contains more a letter more than once (such as 'tree') the programme only returns one index value.
This is a sample of the code:
var chosenWord = "tree";
var individualLetters = chosenWord.split('');
var isLetterThere = individualLetters.indexOf(e);
console.log(isLetterThere);
this code will return the number '2', as that is the first instance of the letter 'e'. How would i get it to return 2 and 3 in the integer format, so that i could use them to replace items in another array using the .splice function.
indexOf takes a second parameter, as the position where it should start searching from.
So my approach would be:
function findLetterPositions(text, letter) {
var positions = new Array(),
pos = -1;
while ((pos = text.indexOf(letter, pos + 1)) != -1) {
positions.push(pos);
}
return positions;
}
console.log(findLetterPositions("Some eerie eels in every ensemble.", "e"));
http://jsfiddle.net/h2s7hk1r/
You could write a function like this:
function indexesOf(myWord, myLetter)
{
var indexes = new Array();
for(var i = 0; i < myWord.length; i++)
{
if(myWord.charAt(i) == myLetter)
{
indexes.push(i);
}
}
return indexes;
}
console.log(indexesOf("tree", "e"));
Loop through it as here:
var chosenWord = "tree";
var specifiedLetter = "e";
var individualLetters = chosenWord.split('');
var matches = [];
for(i = 0;i<individualLetters.length;i++){
if(individualLetters[i] == specifiedLetter)
matches[matches.length] = i;
}
console.log(matches);
An alternative using string methods.
var str = "thisisasimpleinput";
var cpy = str;
var indexes = [];
var n = -1;
for (var i = cpy.indexOf('i'); i > -1; i = cpy.indexOf('i')) {
n += i;
n++;
indexes.push(n);
cpy = cpy.slice(++i);
}
alert(indexes.toString());
var getLetterIndexes = function(word, letter) {
var indexes = [];
word.split("").forEach(function(el, i) {
el === letter && indexes.push(i);
});
return indexes;
};
getLetterIndexes("tree", "e"); // [2, 3]

Returning a string with only vowels capitalized

I'd like to return the variable newString with only vowels capitalized. Not sure how to proceed. Tried using an if/else block but my logic wasn't correct.
function LetterChanges(str) {
var newArray = [];
for (var i = 0; i < str.length; i++) {
var strCode = str.charCodeAt(i) + 1;
var strLetter = String.fromCharCode(strCode);
newArray.push(strLetter);
var newString = newArray.join("");
}
return newString;
}
LetterChanges("hello");
This is different from your approach, but you can do this:
function LetterChanges(str) {
return str.toLowerCase().replace(/[aeiou]/g, function(l) {
return l.toUpperCase();
});
}
console.log(LetterChanges("The Quick Brown Fox Jumped Over The Lazy Dog"));
Here's an approach that's closer to your attempt and uses somewhat simpler concepts:
function LetterChanges(str) {
var newArray = [];
for (var i = 0; i < str.length; i++) {
var ch = str.charAt(i);
if ('aeiouAEIOU'.indexOf(ch) !== -1) {
newArray.push(ch.toUpperCase());
} else {
newArray.push(ch.toLowerCase());
}
}
return newArray.join("");
}
Split, map, join.
var vowels = 'aeiou';
var text = 'my random text with inevitable vowels';
var res = text.split('').map(function(c){
return (vowels.indexOf(c) > -1) ? c.toUpperCase() : c;
});
See the fiddle: http://jsfiddle.net/zo6j89wv/1/
Strings are Collections of word-characters, so you can directly access each part of the string:
var foo = 'bar';
console.log(foo[0]); // outputs 'b'
Hence you can extend this to uppercase the output:
console.log(foo[0].toUpperCase() // outputs 'B'
To do this without regex, you can set the string to lower case, then iterate once over, calling toUpperCase() on each vowel.
function letterChanges(string){
var vowels = 'aeiou';
var lowerString = string.toLowerCase();
var result = '';
for( var i=0; i<lowerString.length; i++){
if( vowels.indexOf( lowerString[i] ) >= 0 ){ //if lowerString[i] is a vowel
result += lowerString[i].toUpperCase();
} else {
result += lowerString[i]
}
}
return result;
}
const vowelSound = string => {
let res = string.split("").filter(item => item === 'a' || item === 'i' || item === 'e' || item === 'o' || item === 'u')
return res.join("")
}

Categories