Multiple of letters count in js - javascript

i have a task:
Count the number of letters “a” in text
Count the number of letters “o” in text
Write the result of multiplying the number of letters “a” and “o”.
is it possible to solve this task in a shorter way??
function countString(str, letter) {
let count = 0;
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) == letter) {
count += 1;
}
}
return count;
}
const string = hello my name is Ola.toLowerCase()
const letterToCheck = "o"
const letterToCheckTwo = "a"
const result = countString(string, letterToCheck);
const resultTwo = countString(string, letterToCheckTwo);
const total = result + resultTwo
console.log(total)

With regular expression function match() will output all the matched conditions.
const string = "hello my name is Ola"
const numberOfA = string.match(/a/gi);
const numberOfO = string.match(/o/gi);
console.log(numberOfA.length * numberOfO.length)

you can do something like this using map filter and reduce
const calculate = (string, letters) => letters
.map(l => string.split('').filter(c => c === l).length)
.reduce((res, item) => res * item)
const string = 'hello my name is Ola'.toLowerCase()
console.log(calculate(string, ['a', 'o']))
console.log(calculate(string, ['a', 'o', 'e']))

You can create an object that stores the count of all the characters and then you can compute the product using this object.
const str = "Avacado";
const charsCount = Array.prototype.reduce.call(
str,
(r, ch) => {
const lowerCh = ch.toLowerCase()
r[lowerCh] ??= r[lowerCh] || 0;
r[lowerCh] += 1;
return r;
},
{}
);
console.log(charsCount["o"] * charsCount["a"]);
Note: Array.prototype.reduce is a generic method, so it can be used with a string as well.

There are a few different approaches. In this post I've programmed in a way that allows you to expand to different letters, different amounts of letters, and different strings easily. There may be better approaches if your problem never needs to change.
In your current code, your are traversing the strings twice and counting the occurrences of the letter within them. You could easily pass the function an array and only traverse the string once:
function countString(str, letters) {
//rather than being a character, letter is now an array of characters
let count = 0;
for (let i = 0; i < str.length; i++) {
if (letters.includes(str.charAt(i))) {
count += 1;
}
}
return count;
}
const string = "hello my name is " + Ola.toLowerCase()
const lettersToCheck = ["o", "a"]
const result = countString(string, lettersToCheck);
//iterate over the array to get the total
var product = 1;
for (int i = 0; i < result.length; i++){
product *= result[i];
}
console.log(product);
Rather than iterating over the strings, however, a different approach is to use regular expressions and .match(). This approach has fewer iterations, although I'm not sure about the low level efficiency, since lots of the comparison is encapsulated by .match():
function countString(str, letters) {
//iterates over the array of letters rather than the string
for (var i = 0; i < letters.length; i++){
count += (str.match(new RegExp(letters[i], "g")) || []).length;
}
return count;
}
const string = "hello my name is " + Ola.toLowerCase()
const lettersToCheck = ["o", "a"]
const result = countString(string, lettersToCheck);
//iterate over the array to get the total
var product = 1;
for (int i = 0; i < result.length; i++){
product *= result[i];
}
console.log(product);
If regex is too much, you could also use .split() to iterate over the characters rather than the string:
function countString(str, letters) {
//iterates over the array of letters rather than the string
for (var i = 0; i < letters.length; i++){
count += str.split(letters[i]).length - 1;
}
return count;
}
const string = "hello my name is " + Ola.toLowerCase()
const lettersToCheck = ["o", "a"]
const result = countString(string, lettersToCheck);
//iterate over the array to get the total
var product = 1;
for (int i = 0; i < result.length; i++){
product *= result[i];
}
console.log(product);
See this post for more information

You can compute the letter frequency each time the phrase changes and then request the product by specifying the letters in the frequency map.
const letterFrequencyMap = (str) =>
str.toLowerCase().split('').reduce((acc, k) =>
acc.set(k, (acc.get(k) ?? 0) + 1), new Map);
const computeProduct = (freq, ...keys) =>
keys.reduce((product, k) => product * (freq.get(k) ?? 1), 1);
// Main
const phrase = "It's about to be noon in a bit.";
const freq = letterFrequencyMap(phrase);
// a = 2; o = 4
console.log(computeProduct(freq, 'a', 'o')); // 8

Related

Calculate duplicated letters next to each other

I need to calculate pairs of same letters that stay each other in twos, I got this code
const calculateLetters = (text: string) => {
const arrayStr = [...text];
let count = 0;
for (let i = 0; i < arrayStr.length; i++) {
if (arrayStr[i] === arrayStr[i + 1]) {
count += 1;
}
}
return count;
};
console.log(calculateLetters('ABBAAA'));
But now it returns 3, what I need to do is make count equal 2, because I need to calculate pairs of same letters.
In your if statement, if you've found a pair, then iterate till the point your i+1th element is not same as ith element, and then count again.
You can accomplish this with RegEx The below pattern will count matches of the same character pairs.
const countPairs = (text) => {
//Grab the matches length if any exist, otherwise return 0
return (text.match(/(.)\1/g))?.length ?? 0
}
console.log(countPairs("abc")) //Result is 0
console.log(countPairs("ABBAAA")) //Result is 2
console.log(countPairs("aabBccDD111")) //Result is 4
With your code, you just need to move past the match when you find one
const calculateLetters = (text) => {
const arrayStr = [...text];
let count = 0;
for (let i = 0; i < arrayStr.length; i++) {
if (arrayStr[i] === arrayStr[i + 1]) {
count += 1;
//move past the match
i++;
}
}
return count;
};
console.log(calculateLetters('ABBAAA'));
const calculateLetters = (text: string) => {
const arrayStr = [...text];
let count = 0;
for (let i = 0; i < arrayStr.length; i++) {
let j = i;
while(i + 1 < arrayStr.length && arrayStr[i] === arrayStr[i + 1]) {
i+= 1;
}
if(i != j){
count += 1;
}
}
return count;
};
console.log(calculateLetters('ABBAAA'));
Define a zip function that can be used to create a list of pairs of adjacent letters in the text. Then count the pairs that match.
Since you want unique adjacent letters, pass the pairs through a Set before counting.
const zip = (a, b) => a.map((k, i) => [k, b[i]])
const calculateLetters = (text) => {
const chars = [...text]
const adjacentLetters = zip(chars, chars.slice(1))
const uniqueAdjacentLetters = [
...new Set(adjacentLetters.map(([a,b]) => `${a}:${b}`))
].map(x => x.split(':'))
const count = uniqueAdjacentLetters.reduce(
(total, [a,b]) => total + ((a === b) ? 1 : 0),
0,
)
return count
}
console.log(calculateLetters('ABBAAA'))

Algorithm for reversing certain characters in long strings

I'm currently working on a programming question:
Given a string s and an integer k, reverse the first k characters for every 2k characters counting from the start of the string.
If there are fewer than k characters left, reverse all of them. If there are less than 2k but greater than or equal to k characters, then reverse the first k characters and leave the other as original.
I created a program that solves the first 45 of the 60 test cases, but apparently falls apart on really long strings. When fed strings of 999 characters, the last few came out as nonsense.
I cannot see any fault in my code that may have caused that. Any feedback? Simple solutions or just better ways of constructing my code?
function reverseArrayOfChars(sArray) {
const length = sArray.length;
let temp;
for (let s = 0; s < length / 2; s++) {
temp = sArray[s];
sArray[s] = sArray[length - 1 - s];
sArray[length - 1 - s] = temp;
}
return sArray;
}
function reverseStr(s, k) {
let sArray = s.split("");
let newArray = []; //Final array to be returned
let tempArray = []; //tempArray is used to store returns from reverseArrayOfChars function. These returns are then concatenated onto newArray.
let switchBoard = 1; //Used to 'switch' between two conditions. Changes automatically every iteration of the loop.
for (let counter = 0; counter < sArray.length; counter += k) {
switchBoard = switchBoard === 0 ? 1 : 0;
if (sArray.length - counter < k) {
tempArray = reverseArrayOfChars(sArray.slice(counter));
newArray = newArray.concat(tempArray);
break;
} else if (sArray.length - counter > k && sArray.length < k * 2) {
tempArray = reverseArrayOfChars(sArray.slice(counter, counter + k));
newArray = newArray.concat(tempArray);
tempArray = sArray.slice(counter + k);
newArray = newArray.concat(tempArray);
break;
} else if (switchBoard === 0) {
tempArray = reverseArrayOfChars(sArray.slice(counter, counter + k));
newArray = newArray.concat(tempArray);
} else if (switchBoard === 1) {
tempArray = sArray.slice(counter, counter + k);
newArray = newArray.concat(tempArray);
}
}
return newArray.join("");
Alternatively, you could try this sample code:
var reverseStr = function(s, k) {
if (k > s.length)
return s.split('').reverse().join('');
const split = s.split('');
// reverse the seg. then join it back
for (let i = 0; i < s.length; i += 2*k) {
const reverse = split.splice(i, k).reverse();
split.splice(i, 0, ...reverse);
}
return split.join('');
};

How to count common characters in two strings in JavaScript?

Given two strings s1 and s2 consisting of lowercase English alphabets, the task is to count all the pairs of indices (i, j) from the given strings such that s1[i] = s2[j] and all the indices are distinct i.e. if s1[i] pairs with some s2[j] then these two characters will not be paired with any other character.
Input: s1 = 'abcd', s2 = 'aad'
Output: 2
Input: s1 = 'geeksforgeeks', s2 = 'platformforgeeks'
Output: 8
I tried to like this:
function getSameCount(str, str2) {
var o = {},
o2 = {};
for (var i = 0; i < str.length - 1; i++) {
if (str[i] in o) {
o[str[i]] = parseInt(o[str[i]] + 1)
} else {
o[str[i]] = 0
}
}
console.log(o);
for (var i = 0; i < str2.length - 1; i++) {
if (str[i] in o2) {
o2[str[i]] = parseInt(o2[str[i]] + 1)
} else {
o2[str[i]] = 0
}
}
console.log(o2);
}
getSameCount('abcd', 'aad')
Use for..in loop and includes method
var s1 = "abcd";
var s2 = "aad";
function match(s1, s2) {
var count = 0;
for(let i in s1) {
s2.includes(s1[i]) ? count++ : false;
}
return count;
}
console.log(match(s1,s2));
We can convert the second input string to an array, then the next step is to iterate over the first input string and find a match in the second input string's character array.
If a match is found, increment the counter and remove that character from the second input string's character array so that it is not considered in the next match:
//Solution:
function getSameCount(str1, str2) {
let count = 0;
const obj = str2.split("");
for(str of str1){
let idx = obj.findIndex(s => s === str);
if(idx >= 0){
count++;
obj.splice(idx, 1);
}
}
return count;
}
//Test:
console.log(getSameCount("abcd", "aad"));
console.log(getSameCount("geeksforgeeks", "platformforgeeks"));
console.log(getSameCount("aad", "abcd"));
console.log(getSameCount("platformforgeeks", "geeksforgeeks"));
You can create a custom method on the array and find the number of characters which are common in all the words. Below steps are list of procedure to find the common characters in all the string
Create a prototype method on Array , findCommonWord in this case.
This method accepts an array of string, so input will be like
[ "abcd", "aad","geeksforgeeksda","platdformforgeeks"].findCommonWord()
First step is to modify the input, to remove duplicate characters from a string using Set then sort it by ascending order of the length of string. This is because number of loop will be least if we have to find common character , that also have to present in string with least length.
Then create a new array with out the first string and split the first string. split will create a new array and iterate over it and check if this character is present in rest of the string.
var s1 = "abcd",
s2 = "aad",
s3 = "geeksforgeeksda",
s4 = "platdformforgeeks";
Array.prototype.findCommonWord = function() {
let tempArray = this.map(function(item) {
return [...new Set(item.split(''))].join('');
}).sort(function(a, b) {
return a.length - b.length
})
let count = 0;
let firstElm = tempArray[0].split('');
let restElem = tempArray.splice(1);
let countObject = {}
for (let i = 0; i < firstElm.length; i++) {
let z = findIfIncludes(restElem, firstElm[i]);
if (z.length === restElem.length) {
countObject[firstElm[i]] = 1;
} else {
countObject[firstElm[i]] = 0
}
}
function findIfIncludes(arr, char) {
return arr.filter(item => item.includes(char))
}
console.log(countObject)
let totalCount = 0;
for (let keys in countObject) {
if (countObject[keys] > 0) {
totalCount += 1;
}
}
return totalCount;
};
console.log([s1, s2, s3, s4].findCommonWord());
function numberOfSameChars(s1, s2) {
let obj = {};
let counter = 0;
for (let i=0; i<s1.length; i++){
if(s1[i] in obj) {
obj[s1[i]]++;
} else {
obj[s1[i]] = 1;
}
}
for (let i=0; i<s2.length; i++) {
if(s2[i] in obj && obj[s2[i]] > 0) {
obj[s2[i]]--;
counter++;
}
}
return counter;
}
Try this code:
function countMatch(s1,s2){
var count = 0;
while(s1.length && s2.length){
if(s2.includes(s1.charAt(0))){
count++;
s2 = s2.replace(s1.charAt(0),"");
s1 = s1.slice(1);
}
else {
s1 = s1.slice(1);
}
}
return count;
}
console.log(countMatch("abcd","aad"));
//2
I used objects to do this, and I was kind of curious about there being another way, as I wanted to take an algorithmic approach as well, but whatever works works.
Anyways, here's the code:
function commonCharacterCount(s1, s2) {
let string1Counter = {};
let string2Counter = {};
let commonCount = 0;
for(let i = 0; i < s1.length; i++){
if(!string1Counter.hasOwnProperty(s1[i])){
string1Counter[s1[i]] = 0;
}
string1Counter[s1[i]]++;
}
for(let i = 0; i < s2.length; i++){
if(!string2Counter.hasOwnProperty(s2[i])){
string2Counter[s2[i]] = 0;
}
string2Counter[s2[i]]++;
}
for(let key in string1Counter){
if(string2Counter.hasOwnProperty(key)){
if(string1Counter[key] < string2Counter[key]){
commonCount += string1Counter[key];
}
else{
commonCount += string2Counter[key];
}
}
}
return commonCount;
}
The logic is basically to just save all of the characters of each string and their count,check for similarities, and compare the count of common characters. Whichever has the fewer amount of common characters will be the amount shared by both.
O(3N) time complexity, O(2N) space complexity (because of the stored objects).
I guess I can also do "delete object" but seems redundant on just an algorithm IDE because it's not like it's running on a server for any extended period of time.
function commonSameCount(s1, s2) {
var s1Array = s1.split("");
var s2Array = s2.split("");
var count = 0;
let index = 0;
s1Array.filter(s1 => {
index = s2Array.findIndex(s2 => s2 == s1);
if(index >= 0){
count++;
s2Array.splice(index, 1);
}
});
return count;
}
A simple solution using Regex:
index.js
function getSameCount(s1,s2) {
for (let i in s2) {
s1 = s1.replace(s2[i], "1")
}
const result = s1.replace(/[^1]/g, '').length
return result
}

make two strings anagrams

i solving a challenge of Hacker rank . it about how anagrams. i give two string input and i have to find ...
Print a single integer denoting the number of characters you must delete to make the two strings anagrams of each other.
i have detected if it's anagrams or not and difference. but now can do the rest of it dont have any ideas.please help.
function main() {
var a = readLine();
var b = readLine();
var sum1 = 0 ;
var sum2 = 0 ;
for (var i= 0; i<= a.length-1; i++ )
{
sum1 = sum1 + a.charCodeAt(i);
}
console.log(sum1);
for (var i= 0; i<= b.length-1; i++ )
{
sum2 = sum2 + b.charCodeAt(i);
}
console.log(sum2);
if(sum1== sum2)
{
console.log("anagrams");
}
else
{
console.log("not anagram");
var diff = sum1 - sum2;
console.log(diff);
/// what to do now ?
}
}
I have solved this on hackerRank by using the object approach to count the frequency of letters if you are still looking for a reasonable solution.
function makeAnagrams(a,b){
let charA=buildcharMap(a)
let charB=buildcharMap(b)
let characters=[]
let counter=0
for(let char in charA){
if(charA[char] && charB[char]){
if(charA[char]===charB[char]){ //same frequency
continue;
}
else{
if(charA[char]>charB[char]){
counter=counter+ charA[char]-charB[char]
}
else{
counter=counter+ charB[char]-charA[char]
}
}
}
else{
counter=counter+charA[char]
}
}
for(let char in charB){
if(charB[char] && charA[char]===undefined){
counter=counter+charB[char]
}
}
return counter;
}
function buildcharMap(str){
var charMap={}
for(let char of str){
if (charMap[char]===undefined){
charMap[char]=1
}
else{
charMap[char]+=1
}
}
return charMap
}
console.log(makeAnagrams('cde','abc'))
I have solve this question on hackerearth, i took slightly different approach.
What i have done in this code is that i have check for all the characters and replace the character with "#" sign if both characters in string are same, then i count all the "#" signs in one of the strings that is modified, and then subtracted value of that count multiplied by two(because... they are similar in both strings) from total length of both strings.
Here is my code, hope you can convert it into javascript. ->
import java.util.Scanner;
public class Anagrams {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int Testcases = scanner.nextInt();
String[] str1 = new String[1000];
String[] str2 = new String[1000];
// Taking input
for (int i = 0; i < Testcases; i++) {
str1[i] = scanner.next().toLowerCase();
str2[i] = scanner.next().toLowerCase();
}
// For Total Length of both strings
int TotalLength[] = new int[Testcases];
// For counting "#" signs
int count[] = new int[Testcases];
// Loop through TestCases
for (int i = 0; i < Testcases; i++) {
TotalLength[i] = str1[i].length() + str2[i].length();
for (int j = 0; j < str1[i].length(); j++) {
for (int k = 0; k < str2[i].length(); k++) {
// If both characters are similar, then replace those characters with "#" signs
if (str1[i].charAt(j) == str2[i].charAt(k)) {
str1[i] = str1[i].replaceFirst(Character.toString(str1[i].charAt(j)),"#");
str2[i] = str2[i].replaceFirst(Character.toString(str2[i].charAt(k)),"#");
}
}
}
}
// Counting "#" signs from one string
for (int i = 0; i < Testcases; i++) {
count[i] = 0;
char[] c1 = str1[i].toCharArray();
for (char c: c1) {
if(c == '#'){
count[i]++;
}
}
}
// Output
for (int i = 0; i < Testcases; i++) {
System.out.println(TotalLength[i] - 2*count[i]);
}
scanner.close();
}
}
You are really just looking for the sum of the differences in later frequency. You can just count the frequencies into an 26 item array (the rules tell you there will only be lower case numbers). Then subtract each array from the other item by item but item and add the whole thing up. Seems like it only need about four lines of code:
function makeAnagram(a, b) {
const makeCountArray = (str) => [...str].reduce((a, c) => (a[c.charCodeAt(0) - 97]++, a), Array(26).fill(0))
let a1 = makeCountArray(a)
let a2 = makeCountArray(b)
return a1.reduce((a, c, i) => a + Math.abs(c - a2[i]), 0)
}
// test case expected: 30
let count = makeAnagram('fcrxzwscanmligyxyvym', 'jxwtrhvujlmrpdoqbisbwhmgpmeoke')
console.log(count)
Here's another approach using Map() of all the 26 alphabets in each string
function makeAnagram(a, b) {
let result = 0;
const alphabets = 'abcdefghijklmnopqrstuvwxyz'.split('');
const getCharCountMap = str => {
const strArray = str.split('');
let charMap = new Map();
alphabets.forEach(alphabet => charMap.set(alphabet, 0));
strArray.forEach(letter => charMap.set(letter, charMap.get(letter) + 1));
return charMap;
};
const aMap = getCharCountMap(a);
const bMap = getCharCountMap(b);
alphabets.forEach(alphabet => {
result = result + Math.abs(aMap.get(alphabet) - bMap.get(alphabet));
});
return result;
}

Is it possible to get similar text in string by RegExp

Is it possible to make function to get similar text in string by RegExp ex:
max differences letters = 2 or 3 letters ( + or - ) will be fine
var text = 'Is it possible to get similar text in string by RegExp';
// and
similar_text(text , 'strong'); // => string
similar_text(text , 'posible'); // => possible
similar_text(text , 'isit'); // => Is it
// etc...
Here is my implementation (Needs Levenshtein function). Not the prettiest, but it gets the job done:
function similar_text(haystack, needle) {
let best_match = false;
let best_match_int = 9007199254740992;
let arr = haystack.split(" ");
haystack = haystack.toLowerCase();
needle = needle.toLowerCase();
for (let chunk = 1; chunk < arr.length; chunk++) {
for (let i = 0; i < arr.length; i++) {
if (i + chunk > arr.length)
continue;
let subStack = arr.slice(i, i + chunk).join(' ');
let l = new Levenshtein(needle, subStack);
if (l.distance < best_match_int) {
best_match = subStack;
best_match_int = l.distance;
}
}
}
return best_match;
}
Here is a Plunker demoing how this can be done.

Categories

Resources