Is it possible to get similar text in string by RegExp - javascript

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.

Related

Multiple of letters count in js

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

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
}

how to get common substring in two string to maintain order?

I am trying to do one problem of hackerrank but I am not able to solve that problem
Can someone please help me with wrong logic implementation done by me?
problem
Print the length of the longest string, such that is a child of both s1 and s2.
Sample Input
HARRY
SALLY
Sample Output
2
Explanation
The longest string that can be formed by deleting zero or more characters from HARRY and SALLY is AY, whose length is 2.
Sample Input 1
AA
BB
Sample Output 1
0
Explanation 1
AA and BB have no characters in common and hence the output is 0
Sample Input 2
SHINCHAN
NOHARAAA
Sample Output 2
3
Explanation 2
The longest string that can be formed between SHINCHAN and NOHARAAA while maintaining the order is NHA.
I have written some logic which is as follows:
function commonChild(s1, s2) {
var arr = s2.split(),
currenString = '',
maxLength = 0,
index = -1;
console.log(arr);
for (var i = 0; i < s1.length; i++) {
var char = s1.charAt(i),
charIndex = arr.indexOf(char);
console.log(char)
if (index < charIndex) {
index = charIndex;
currenString +=char;
}
maxLength= Math.max(maxLength,currenString.length)
}
return maxLength;
}
commonChild('ABCDEF', 'FBDAMN');
console.log(commonChild('ABCDEF', 'FBDAMN'));
pardon me. this is an unoptimized solution.
function maxCommon(a, b, offset) {
offset = offset || 0;
if (a === b) {
return [[a, b]];
}
var possibleSolns = [];
for (var i = 0 + offset; i < a.length; i++) {
for (var j = 0 + offset; j < b.length; j++) {
if (a.charAt(i) === b.charAt(j)) {
possibleSolns.push([
a.substring(0, offset) + a.substring(i),
b.substring(0, offset) +b.substring(j)
]);
break;
}
}
}
var results = [];
possibleSolns.forEach(function(soln) {
var s = maxCommon(soln[0], soln[1], offset+1);
if (s.length === 0) {
s = [[soln[0].substring(0, offset +1), soln[1].substring(0, offset +1)]];
}
results = results.concat(s);
});
return results;
}
var maxLen = -1;
var soln = null;
maxCommon("ABCDEF", "FBDAMN").map(function(_) {
return _[0];
}).forEach(function(_) {
if (_.length > maxLen) {
soln = _;
maxLen = _.length;
}
});
console.log(soln);
I kept most of your logic in the answer:
function commonChild(s1, s2) {
var // Sets strings to arrays
arrayString1 = s1.split(""),
arrayString2 = s2.split(""),
collectedChars = "",
maxLength = 0,
max = arrayString1.length;
for (var i = 0; i < max; i++) {
var
char = arrayString1[i],
count = arrayString2.indexOf(char);
// check if char is in second string and not in collected
if (count != -1 && collectedChars.indexOf(char) == -1) {
collectedChars += char;
maxLength++;
}
}
return maxLength;
}
// expected output 4
console.log(commonChild(
'ABCDEF',
'FBDAMN'
));
// expected output 1
console.log(commonChild(
'AA',
'FBDAMN'
));
Using lodash and spread operation you can do it in this way.
const test = (first, second) => {
const stringArray1 = [...first];
const stringArray2 = [...second];
return _.intersection(stringArray1, stringArray2).length;
}
console.log(test('ABCDEF', 'FBDAMN'));
You can solve it using lcs least common subsequence
function LCS(s1,s2,x,y){
var result = 0;
if(x==0 || y==0){
result = 0
}else if(s1[x-1] == s2[y-1]){
result = 1+ LCS(s1,s2,x-1,y-1)
} else if(s1[x-1] != s2[y-1]){
result = Math.max(LCS(s1,s2,x-1,y), LCS(s1,s2,x,y-1))
}
return result;
}
// Complete the commonChild function below.
function commonChild(s1, s2) {
return LCS(s1,s2,s1.length,s2.length);
}
Based on your code before the edit.
One little change is to change var arr = s2.split() to split('').
The main change in the logic is that I added a loop to run over the string each time from another character (first loop from the first, second from the second etc).
function commonChild(s1, s2) {
var arr = s2.split(''),
currenString = '',
maxLength = 0,
index = -1,
j = -1;
for (var ii = 0; ii < s1.length; ii++) {
index = -1;
currenString = '';
for (var i = ii; i < s1.length; i++) {
var char = s1.charAt(i),
j = arr.indexOf(char);
if (index < j) {
index = j;
currenString += char;
}
maxLength = Math.max(maxLength, currenString.length)
}
}
return maxLength;
}
console.log(commonChild('ABCDEF', 'FBDAMN'));

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;
}

How to combine(iterate through) 2 strings, mixing the letters in each string with different length?

EDIT: the output should be t1h2i3s4oneislong
I am trying to write a function which returns a new word,combining the letters in 2 words which are being passed the function. If word1 is hello and word2 is 12345 it should return h1e2l3l4o5 - mixing all the letters of both words. The problem is that if one word is longer than the other one, I get undefinedundefinedundefined etc.
I thought I could just ask is word1.length
Could someone explain why this doesn't work (I am learning) and how I could make it work? Thank you!!
function creatingWord(one,two){
var string="";
if (one.length==1 &&two.length==1){
string=one+two;
}
else if (one.length==two.length){
for (var i=0; i<one.length;i++){
string+=one[i];
string+=two[i];
}
}
else if (one.length>two.length){
for (var j=0; j<one.length;j++){
string+=one[j];
string+=two[j];
}
}
return string;
}
var result = creatingWord('thisoneislong', '1234');
result;
I think there are two possible answers here, depending on what you want the output to be.
This code gives you the output "t1h2i3s4oneislong" (continuing with the longer word once the other is exhausted):
function creatingWord(one, two){
var output = "";
for (var i = 0; i < Math.max(one.length, two.length); i++) {
if (one.length > i) {
output += one[i];
}
if (two.length > i) {
output += two[i];
}
}
return output;
}
Here's a version that gives the output "t1h2i3s4" (truncates to the smaller word):
function creatingWord(one, two){
var output = "";
for (var i = 0; i < Math.min(one.length, two.length); i++) {
output += one[i] + two[i];
}
return output;
}
EDIT:
Answering a comment below, here's a version that takes a variable number of arguments:
// takes a variable number of words
function creatingWord() {
var output = "";
var words = Array.prototype.slice.call(arguments);
var maxLength = 0;
for (var i = 0; i < words.length; i++) {
var word = words[i];
if (word.length > maxLength) {
maxLength = word.length;
}
}
for (var i = 0; i < maxLength; i++) {
for (var j = 0; j < words.length; j++) {
var word = words[j];
if (word.length > i) {
output += word[i];
}
}
}
return output;
}
Using the length of the first word as the output length, we can use the following to combine any list of words as stated in your question:
var zip = array => array[0].map((a, i) => array.map(a => a[i]));
var words = ["aaaa", "bbb", "cc", "d", "........"],
combined = zip(words.map(w => w.split(""))).map(a => a.join("")).join("");
console.log(combined); // abcd.abc.ab.a.
You can split your words to work with Array functions like reduce(), code looks cleaner :
let a = 'HelloWorld';
let b = '_____';
let combineWords = (a,b) => b.split('') &&
a.split('').reduce((prev, curr, i)=> prev += curr + (b[i] || ''),'');
console.log(combineWords(a, b));
console.log(combineWords(b, a));
Note that combineWords(a, b) will return all the posible combinations attending to a.length

Categories

Resources