check if text is a subset of a serie - javascript

I want to check a string against a serie (with respect to order)
-- see bellow for another approach (string solution)
Regex solution (potential)
r = /[(qw|az)ert(y|z)uiop(é|å|ü)]{5}/g
str = "ertyu"
str2 = "rtrrr"
r.test(str) // true
r.test(str2) // true
The str2 should return false as it does not respect the order, but since the regex is wrapped in array I assume this approach is wrong from the start.
String solution
arr = [
"qwertyuiopé",
"qwertyuiopå",
"qwertyuiopü",
"qwertzuiopé",
"qwertzuiopå",
"qwertzuiopü",
"azertyuiopé",
"azertyuiopå",
"azertyuiopü",
"azertzuiopé",
"azertzuiopå",
"azertzuiopü",
]
str = "ertyu"
str2 = "yrwqp"
function test(s) {
for (const a of arr) {
if (a.indexOf(str) >= 0) return true
}
return false
}
test(str) // true
test(str2) // false
The string version works but its ugly and big
Is there a way with regex to get this working?

In the end I don't think what I want to achieve would be possible,
I ended up with an array of different series (keyboard trivial series)
and a function that checks if a password is a sequence of the series
const trivialseries = [
// swedish, german, french, english, spanish, italian - keyboard
"1234567890",
"qwertyuiopå", // se|en
"asdfghjklöä",
"zxcvbnm",
"qwertzuiopü", // de
"yxcvbnm",
"azertyuiop", // fe
"qsdfghjklmù",
"wxcvbn",
"asdfghjklñ", // sp
"qwertyuiopé", // it
"asdfghjklòàù",
];
const MAX = 5;
function subsequence(serie, str) {
for (let i = 0; i < str.length - MAX + 1; i++) {
const index = serie.indexOf(str[i]);
if (index >= 0) {
let found = 1;
for (let j = i + 1; j < str.length; j++) {
if (serie[index + found] === str[j]) found++;
else break;
if (found === MAX) return true;
}
}
}
return false;
}
function isTrivial(password) {
for (let ts of trivialseries) {
if (subsequence(ts, password)) return true;
const reverse = ts.split("").reverse().join("");
if (subsequence(reverse, password)) return true;
}
return false;
}
console.log(isTrivial("e927ncsmnbvcdkeloD€%s567jhdoewpm")); // true "mnbvc" is reverse form of "cvbnm"

Related

How can I extract all contained characters in a String? [duplicate]

I have a string with repeated letters. I want letters that are repeated more than once to show only once.
Example input: aaabbbccc
Expected output: abc
I've tried to create the code myself, but so far my function has the following problems:
if the letter doesn't repeat, it's not shown (it should be)
if it's repeated once, it's show only once (i.e. aa shows a - correct)
if it's repeated twice, shows all (i.e. aaa shows aaa - should be a)
if it's repeated 3 times, it shows 6 (if aaaa it shows aaaaaa - should be a)
function unique_char(string) {
var unique = '';
var count = 0;
for (var i = 0; i < string.length; i++) {
for (var j = i+1; j < string.length; j++) {
if (string[i] == string[j]) {
count++;
unique += string[i];
}
}
}
return unique;
}
document.write(unique_char('aaabbbccc'));
The function must be with loop inside a loop; that's why the second for is inside the first.
Fill a Set with the characters and concatenate its unique entries:
function unique(str) {
return String.prototype.concat.call(...new Set(str));
}
console.log(unique('abc')); // "abc"
console.log(unique('abcabc')); // "abc"
Convert it to an array first, then use Josh Mc’s answer at How to get unique values in an array, and rejoin, like so:
var nonUnique = "ababdefegg";
var unique = Array.from(nonUnique).filter(function(item, i, ar){ return ar.indexOf(item) === i; }).join('');
All in one line. :-)
Too late may be but still my version of answer to this post:
function extractUniqCharacters(str){
var temp = {};
for(var oindex=0;oindex<str.length;oindex++){
temp[str.charAt(oindex)] = 0; //Assign any value
}
return Object.keys(temp).join("");
}
You can use a regular expression with a custom replacement function:
function unique_char(string) {
return string.replace(/(.)\1*/g, function(sequence, char) {
if (sequence.length == 1) // if the letter doesn't repeat
return ""; // its not shown
if (sequence.length == 2) // if its repeated once
return char; // its show only once (if aa shows a)
if (sequence.length == 3) // if its repeated twice
return sequence; // shows all(if aaa shows aaa)
if (sequence.length == 4) // if its repeated 3 times
return Array(7).join(char); // it shows 6( if aaaa shows aaaaaa)
// else ???
return sequence;
});
}
Using lodash:
_.uniq('aaabbbccc').join(''); // gives 'abc'
Per the actual question: "if the letter doesn't repeat its not shown"
function unique_char(str)
{
var obj = new Object();
for (var i = 0; i < str.length; i++)
{
var chr = str[i];
if (chr in obj)
{
obj[chr] += 1;
}
else
{
obj[chr] = 1;
}
}
var multiples = [];
for (key in obj)
{
// Remove this test if you just want unique chars
// But still keep the multiples.push(key)
if (obj[key] > 1)
{
multiples.push(key);
}
}
return multiples.join("");
}
var str = "aaabbbccc";
document.write(unique_char(str));
Your problem is that you are adding to unique every time you find the character in string. Really you should probably do something like this (since you specified the answer must be a nested for loop):
function unique_char(string){
var str_length=string.length;
var unique='';
for(var i=0; i<str_length; i++){
var foundIt = false;
for(var j=0; j<unique.length; j++){
if(string[i]==unique[j]){
foundIt = true;
break;
}
}
if(!foundIt){
unique+=string[i];
}
}
return unique;
}
document.write( unique_char('aaabbbccc'))
In this we only add the character found in string to unique if it isn't already there. This is really not an efficient way to do this at all ... but based on your requirements it should work.
I can't run this since I don't have anything handy to run JavaScript in ... but the theory in this method should work.
Try this if duplicate characters have to be displayed once, i.e.,
for i/p: aaabbbccc o/p: abc
var str="aaabbbccc";
Array.prototype.map.call(str,
(obj,i)=>{
if(str.indexOf(obj,i+1)==-1 ){
return obj;
}
}
).join("");
//output: "abc"
And try this if only unique characters(String Bombarding Algo) have to be displayed, add another "and" condition to remove the characters which came more than once and display only unique characters, i.e.,
for i/p: aabbbkaha o/p: kh
var str="aabbbkaha";
Array.prototype.map.call(str,
(obj,i)=>{
if(str.indexOf(obj,i+1)==-1 && str.lastIndexOf(obj,i-1)==-1){ // another and condition
return obj;
}
}
).join("");
//output: "kh"
<script>
uniqueString = "";
alert("Displays the number of a specific character in user entered string and then finds the number of unique characters:");
function countChar(testString, lookFor) {
var charCounter = 0;
document.write("Looking at this string:<br>");
for (pos = 0; pos < testString.length; pos++) {
if (testString.charAt(pos) == lookFor) {
charCounter += 1;
document.write("<B>" + lookFor + "</B>");
} else
document.write(testString.charAt(pos));
}
document.write("<br><br>");
return charCounter;
}
function findNumberOfUniqueChar(testString) {
var numChar = 0,
uniqueChar = 0;
for (pos = 0; pos < testString.length; pos++) {
var newLookFor = "";
for (pos2 = 0; pos2 <= pos; pos2++) {
if (testString.charAt(pos) == testString.charAt(pos2)) {
numChar += 1;
}
}
if (numChar == 1) {
uniqueChar += 1;
uniqueString = uniqueString + " " + testString.charAt(pos)
}
numChar = 0;
}
return uniqueChar;
}
var testString = prompt("Give me a string of characters to check", "");
var lookFor = "startvalue";
while (lookFor.length > 1) {
if (lookFor != "startvalue")
alert("Please select only one character");
lookFor = prompt(testString + "\n\nWhat should character should I look for?", "");
}
document.write("I found " + countChar(testString, lookFor) + " of the<b> " + lookFor + "</B> character");
document.write("<br><br>I counted the following " + findNumberOfUniqueChar(testString) + " unique character(s):");
document.write("<br>" + uniqueString)
</script>
Here is the simplest function to do that
function remove(text)
{
var unique= "";
for(var i = 0; i < text.length; i++)
{
if(unique.indexOf(text.charAt(i)) < 0)
{
unique += text.charAt(i);
}
}
return unique;
}
The one line solution will be to use Set. const chars = [...new Set(s.split(''))];
If you want to return values in an array, you can use this function below.
const getUniqueChar = (str) => Array.from(str)
.filter((item, index, arr) => arr.slice(index + 1).indexOf(item) === -1);
console.log(getUniqueChar("aaabbbccc"));
Alternatively, you can use the Set constructor.
const getUniqueChar = (str) => new Set(str);
console.log(getUniqueChar("aaabbbccc"));
Here is the simplest function to do that pt. 2
const showUniqChars = (text) => {
let uniqChars = "";
for (const char of text) {
if (!uniqChars.includes(char))
uniqChars += char;
}
return uniqChars;
};
const countUnique = (s1, s2) => new Set(s1 + s2).size
a shorter way based on #le_m answer
let unique=myArray.filter((item,index,array)=>array.indexOf(item)===index)

Check if String has sequential or repeated characters in javascript (underscore)

I have code that I am trying to refactor. Im new to javascript so Im tring to make more readable code using functions in libraries like underscore.
The function below can detect when string
contains 3 or more ordered characters such as (234, efg, LmN)
and
when string contains 3 or more repeated (lll, 444, MMm, ###)
const input = "Dfdf123125";
const myStr = input.toLowerCase();
const n = 3;
let isRepeating = false;
let isSequential = false;
for (let i = 0; i < myStr.length; i++) {
if (i + (n - 1) <= myStr.length) {
let isRepeatingTemp = false;
let isSequentialTemp = false;
for (let j = i; j < i + n; j++) {
(myStr.charCodeAt(i) === myStr.charCodeAt(j)) ? isRepeatingTemp = true: isRepeatingTemp = false;
(myStr.charCodeAt(i) === myStr.charCodeAt(j) - (n - 1)) ? isSequentialTemp = true : isSequentialTemp = false;
}
if (isRepeatingTemp) isRepeating = true;
if (isSequentialTemp) isSequential = true;
}
}
Im trying to to see if I can optimize this and make it more readable with underscore and/or even make time/space complexity better. I know this can also be done with regx but im trying to get it done without it.
Instead of the inner for loop, I chunked the string to n using Array.prototype.slice() to see ahead n characters. I used Array.prototype.indexOf() to find if it's sequential based off the abc and num constants(ref). To see if it's repeating, I used Array.prototype.every() that loops through the chunk and check if they're similar and return a boolean based on the expression.
The result gives the output of each instance found, and if it was sequential or repeating.
const input = "Dfdf123125";
function RepSeq(str, n) {
var rep = false;
var seq = false;
var result = [];
const num = '0123456789';
const abc = 'abcdefghijklmnopqrstuvqxyz';
if (str.length < n) return false;
for (var i = 0; i < str.length; i++) {
if (i + n > str.length) break;
var chunk = str.slice(i, i + n);
var seqABC = abc.indexOf(chunk) > -1;
var seq123 = num.indexOf(chunk) > -1;
if (seq123 || seqABC) {
seq = true;
result.push(chunk);
}
if ([...chunk].every(v => v.toLowerCase() === chunk[0].toLowerCase())) {
rep = true;
result.push(chunk);
}
}
return {
repetition: rep,
sequential: seq,
out: result
};
}
console.log(RepSeq(input, 3));
// Output:
// {
// out: ["123"],
// repetition: false,
// sequential: true
// }
With this method, we're peeking at the string one block(i+n) at a time. Ex(n=3):
1. [Dfd]f123125
2. D[fdf]123125
3. Df[df1]23125
4. Dfd[f12]3125
5. Dfdf[123]125 - Sequential!
6. Dfdf1[231]25
7. Dfdf12[312]5
8. Dfdf123[125]

understanding solution of Valid Anagram in javascript

This is from LeetCode - Valid Anagram
Given two strings s and t , write a function to determine if t is an anagram of s.
Example 1:
Input: s = "anagram", t = "nagaram"
Output: true
Example 2:
You may assume the string contains only lowercase alphabets.
Follow up:
What if the inputs contain unicode characters? How would you adapt your solution to such case?
I don't understand these code below
result1[s.charCodeAt(i) - 97]++; --> what does ++ mean?
result2.length = 26; --> what does 26 stand for?
result2.fill(0); --> why does fill it with 0?
Please advise!
var isAnagram = function(s,t) {
if (s.length !== t.length)
result false;
const result1 = [];
result1.length = 26;
result1.fill(0);
const result2 = [];
result2.length = 26;
result2.fill(0);
for (let i = 0; i < s.length; i++) {
result1[s.charCodeAt(i) - 97]++;
result2[t.charCodeAt(i) - 97]++;
}
for (let i = 0; i < result1.length; i++) {
if (result1[i] !== result2[i]) {
return false;
}
}
return true;
};
This is my version of solution. You can even skip join() at the end of, since you still can compare two arrays.
const isAnagram = function(s, t) {
if (s.length !== t.length ) {
return false;
}
if (s.split('').sort().join('') === t.split('').sort().join('')) {
return true;
} else {
return false;
}
};
That's somewhat poorly written code, let's improve it a bit so that your question are automatically gone:
var isAnagram = function(string1, string2) {
if (string1.length !== string2.length)
return false; // here was a typo: result false
const alphabetLength = 26;
// returns an array of zeros (empty counters), each one per alphabet letter
const getAlphabetCounters = function() {
const counters = [];
counters.length = alphabetLength;
counters.fill(0);
return counters;
}
const countCharacter = function(c, counters) {
// zero for a, 25 for z
const position = c.charCodeAt(0) - 97;
counters[position]++;
}
const counters1 = getAlphabetCounters();
const counters2 = getAlphabetCounters();
for (let i = 0; i < string1.length; i++) {
countCharacter(string1[i], counters1);
countCharacter(string2[i], counters2);
}
for (let i = 0; i < counters1.length; i++) {
if (counters1[i] !== counters2[i]) {
return false;
}
}
return true;
};
But it would be probably a better idea to use a "decremental" approach like this:
var isAnagram = function(string1, string2) {
if (string1.length !== string2.length)
return false;
let letters1 = string1.split('');
let letters2 = string2.split('');
for (let letter of letters1) {
let position = letters2.indexOf(letter);
if(position == -1)
return false;
letters2.splice(position, 1);
}
return true;
};
or if one cares about performance for long strings, sorting letters in those and direct comparison would be the way to go.
++ is the increment operator. It can be pre-fixed (++i) or post-fixed (i++). In this case it is post-fixed: result1[s.charCodeAt(i) - 97]++, so it will increment the value of result1[s.charCodeAt(i) - 97].
26 is simply the number of letters in the alphabet (from a to z).
The code is initializing the two arrays and filling them with 0s so that it can use the array elements as counters. Each index in the array represents a letter in the alphabet, and stores the occurrences of that letter in the string.
With minimum array functions and single for loop
function anagram(str1, str2){
var isAnagram = true;
if(str1.length != str2.length){
isAnagram = false;
}else{
for(var i=0; i<str1.length;i++) {
if(!str2.includes(str1[i])){
isAnagram = false;
}
}
}
if(isAnagram){
console.log('string is anagram');
}else{
console.log('string is not anagram');
}
}
anagram('READ', 'DEAR');
var isAnagram = function(s, t) {
let sArr = s.split('');
let tArr = t.split('');
sArr.sort();
let newS = sArr.join('');
tArr.sort();
let newT = tArr.join('');
return newS === newT;
};
this can be done with only one line of code
const isAnagram = (s,t) => ( [...s].sort().join('') === [...t].sort().join(''))
console.log( isAnagram('anagram', 'nagaram') ) // true
console.log( isAnagram('anagram', 'xxx') ) // false
But your question also asks to manage all the utf8 characters, which I interpret as having to eliminate the diacritic, for which there is an answer here.
that I changed a little to keep only the letters (which excludes spaces and other punctuation), to be able to process anagrams of several words too :
const letters = str => str.toLowerCase().normalize("NFD").replace(/[^a-z]/g, '')
which has for final solution:
const letters = str => str.toLowerCase().normalize("NFD").replace(/[^a-z]/g, '')
, isAnagram = (s,t) => ( [...letters(s)].sort().join('') === [...letters(t)].sort().join(''))
;
const anagrams = // from wikipedia
[ { s: "evil"
, t: "vile" }
, { s: "New York Times"
, t: "monkeys write" }
, { s: "William Shakespeare"
, t: "I am a weakish speller" }
, { s: "Le commandant Cousteau"
, t: "Tout commença dans l'eau" } // with diacritic ( French )
, { s: "La crise économique"
, t: "Le scénario comique" }
, { s: "Question sans réponse"
, t: "Enquêtons sans espoir" }
, { s: "Chauve-souris"
, t: "Souche à virus" }
]
for (let {s,t} of anagrams )
console.log(`_${s}_, _${t}_ -> `, isAnagram(s,t))
.as-console-wrapper { max-height: 100% !important; top: 0; }

print like this * "a" -> "a1" * "aabbbaa" -> "a2b3a2"

I am new to js.
can you tell me how to print like this * "a" -> "a1" * "aabbbaa" -> "a2b3a2"
i tried with hash map but test cases failing.
providing my code below.
i am not good in hash map.
can you tell me how to solve with hash map so that in future I can fix it my self.
not sure what data structure to use for this one.
providing my code below.
const _ = require("underscore");
const rle = ( input ) => {
console.log("input--->" + input);
//var someString ="aaa";
var someString = input;
var arr = someString.split("");
var numberCount = {};
for(var i=0; i< arr.length; i++) {
var alphabet = arr[i];
if(numberCount[alphabet]){
numberCount[alphabet] = numberCount[alphabet] + 1;
}
else{
numberCount[alphabet] = 1;
}
}
console.log("a:" + numberCount['a'], "b:" + numberCount['b']);
}
/**
* boolean doTestsPass()
* Returns true if all the tests pass. Otherwise returns false.
*/
/**
* Returns true if all tests pass; otherwise, returns false.
*/
const doTestsPass = () => {
const VALID_COMBOS = {"aaa": "a3", "aaabbc":"a3b2c1"};
let testPassed = true;
_.forEach(VALID_COMBOS, function(value, key) {
console.log(key, rle(key));
if (value !== rle(key)) {
testPassed = false;
}
});
return testPassed;
}
/**
* Main execution entry.
*/
if(doTestsPass())
{
console.log("All tests pass!");
}
else
{
console.log("There are test failures.");
}
You could
match groups of characters,
get the character and the count and
join it to a string.
function runLengthEncoding(string) {
return string
.match(/(.)\1*/g) // keep same characters in a single string
.map(s => s[0] + s.length) // take first character of string and length
.join(''); // create string of array
}
console.log(['a', 'aaa', 'aaabbc'].map(runLengthEncoding));
This is a bit more understandable version which iterates the given string and count the characters. If a different character is found, the last character and count is added to the result string.
At the end, a check is made, to prevent counting of empty strings and the last character cound is added to the result.
function runLengthEncoding(string) {
var result = '',
i,
count = 0,
character = string[0];
for (i = 0; i < string.length; i++) {
if (character === string[i]) {
count++;
continue;
}
result += character + count;
character = string[i];
count = 1;
}
if (count) {
result += character + count;
}
return result;
}
console.log(['', 'a', 'aaa', 'aaabbc'].map(runLengthEncoding));
You can reduce the array into a multidimensional array. map and join the array to convert to string.
const rle = (input) => {
return input.split("").reduce((c, v) => {
if (c[c.length - 1] && c[c.length - 1][0] === v) c[c.length - 1][1]++;
else c.push([v, 1]);
return c;
}, []).map(o => o.join('')).join('');
}
console.log(rle("a"));
console.log(rle("aabbbaa"));
console.log(rle("aaaaaa"));
Your function rle doesn't return a result.
Also note, this implementation may pass the test cases you wrote, but not the examples you mentioned in your question: for the string "aabbaa" this will produce "a4b2", not " a2b2a2" .
A simpler solution:
function runLengthEncoding(str) {
let out = "";
for (let i = 0; i < str.length; ++i) {
let temp = str[i];
let count = 1;
while (i < str.length && str[i+1] == temp) {
++count;
++i;
}
out += temp + count;
} // end-for
return out;
}
console.log(runLengthEncoding("a"));
console.log(runLengthEncoding("aabbbaa"));
console.log(runLengthEncoding("aaaaaa"));

JS - Create smart auto complete

Given a sorted Array of Strings, and user input I need to return the most relevant result.
Example: Array =['Apple','Banana and Melon','Orange'] and user input = 'Mellllon' the returned value should be 'Banana and Melon'
I'm looking for the right algorithm to implement an efficient auto complete solution, and not an out of the box one.
Levenshtein distance seems to be right for this problem. You need to calculate distance between every word in array, check it out
function findClosestString(arr, inputvalue) {
let closestOne = "";
let floorDistance = 0.1;
for (let i = 0; i < arr.length; i++) {
let dist = distance(arr[i], inputvalue);
if (dist > floorDistance) {
floorDistance = dist;
closestOne = arr[i];
}
}
return closestOne;
}
function distance(val1, val2) {
let longer, shorter, longerlth, result;
if (val1.length > val2.length) {
longer = val1;
shorter = val2;
} else {
longer = val2;
shorter = val1;
}
longerlth = longer.length;
result = ((longerlth - editDistance(longer, shorter)) / parseFloat(longerlth));
return result;
}
function editDistance(val1, val2) {
val1 = val1.toLowerCase();
val2 = val2.toLowerCase();
let costs = [];
for(let i = 0; i <= val1.length; i++) {
let lastVal = i;
for(let j = 0; j <= val2.length; j++) {
if (i === 0) {
costs[j] = j;
} else if (j > 0) {
let newVal = costs[j - 1];
if (val1.charAt(i - 1) !== val2.charAt(j - 1)) {
newVal = Math.min(Math.min(newVal, lastVal), costs[j]) + 1;
}
costs[j - 1] = lastVal;
lastVal = newVal;
}
}
if (i > 0) { costs[val2.length] = lastVal }
}
return costs[val2.length];
}
findClosestString(['Apple','Banana and Melon','Orange'], 'Mellllon');
One possible solution is to:
1) convert every value into some simple code (using the same simple rules e.g. convert uppercase char to lowercase, if a char is the same as the previews, it won't get written and so on..) so you have ['aple','banana and melon', 'orange']
2) then you convert the user input , Mellllon => melon
3) now you can simple run
return match_array.filter((x) => {
x.indexOf(match_input)!=-1)
);
Well, as elaborately explained in the topic cited in my comment a fuzzy search regex might come handy provided you have all letters of the searched string (case insensitive "m", "e", "l", "o", "n") are present in the input string in the order of appearance. So according to the generated /M[^e]*e[^l]*l[^o]*o[^n]*n/i regexp from "Melon", "Maellion", "MElllloon" or "nMelrNnon" should all return true.
function fuzzyMatch(s,p){
p = p.split("").reduce((a,b) => a+'[^'+b+']*'+b);
return RegExp(p,"i").test(s);
}
var arr = ['Apple','Banana and Melon','Orange'],
inp = "MaellL;loin",
res = arr.filter(s => s.split(" ").some(w => fuzzyMatch(inp,w)));
console.log(res);
Combining the fuzzyMatch function with a trie type data structure you might in fact obtain quite reasonable elastic auto complete functionality.

Categories

Resources