javascript check if string has one of set of chars - javascript

I am implementing password complexity checks, one of the requirement is that the password contain at least one of a set of chars.
How would I code an efficient function for that, ie
function hasOneOfChars(s, chars) {
// assuming both s and chars are strings,
// return true iff s has at least one char from chars
}
Thanks!

Instead of using above function Try using it with regex pattern.
Contain at least 8 characters
contain at least 1 number
contain at least 1 lowercase character (a-z)
contain at least 1 uppercase character (A-Z)
contains only 0-9a-zA-Z
if you want set of chars use below pattern, Moreover {4,} is atleast contain charaters so you can change according to your requirement
[a-zA-Z0-9]{4,}
Thanks.

By requirement to receive 2 strings arguments. You can try the following code.
function hasOneOfChars(s, chars) {
for (let char of chars.split(""))
if (s.indexOf(char) > -1) {
return true;
}
return false;
}
console.log(hasOneOfChars("abcd", "abcasjkdf"));

You could sort both strings and do the following :
var s = "ytreza".split("").sort().join("");
var chars = "ytrewq".split("").sort().join("");
hasOneOfChars(s, chars); // true
function hasOneOfChars (s, chars) {
var a, b, i, n;
if (s < chars) a = s, b = chars;
else a = chars, b = s;
i = 0, n = a.length;
while (i < n && a[i] !== b[0]) i++;
return i < n;
}
Using sorted strings you don't have to loop through both strings anymore :
a = "azerty"
b = "qwerty"
n = a.length // 6
i = 0, a[i] === b[0] ? false
i = 1, a[i] === b[0] ? false
i = 2, a[i] === b[0] ? true
return i < n // true

(credit goes to CertainPerformance)
// assuming both s and chars are strings, return true iff s has at least one char from chars
function hasOneOfChars(s, chars) {
return [...chars].some(char => s.includes(char));
}
console.log(hasOneOfChars('aaabbb', 'ax'));
console.log(hasOneOfChars('aaabbb', 'xy'));

Related

Write a method strange_words that accepts an array of strings

Write a method strange_words that accepts an array of strings. The method should return an array containing all strings that are either shorter than 6 characters or begin with "e", but are not both shorter than 6 character and also begin with "e".
The last part of the question just confuses me. This is what I have so far:
function strangeWords(words) {
for(var i = 0; i < words.length; i++) {
if (words.length < 6 && words[0] !== "e" || words[0] === "e" && words.length > 6) {
}
return words;
}
}
I assume "words" is an array of strings.
You need to create an empty array, fill it up with words that matches your condition, then return the array. An example will be as follows.
function strangeWords(words) {
let filteredWords = [];
for(var i = 0; i < words.length; i++) {
if (words[i].length < 6 && words[i][0] !== "e" || words[i][0] === "e" && words[i].length > 6) {
filteredWords.push(words[i])
}
}
return filteredWords;
}
A better way is to use the filter function, which returns only strings that matches your condition. Example as follows.
function strangeWords(words) {
return words.filter(word => {
//Your conditions here.
return (word.length < 6 && word[0] !== "e" || word[0] === "e" && word.length > 6)
}
}
Documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
The question is essentially asking you to filter the provided input array based on the conditions given to you.
What you need to do now is to create a new array, and push the values that satisfy those conditions inside your if block.
output.push(words[i]);
return output;
Using Regex filter words that are:
Start with e and has a length greater than 6
Does not start with e and length less than or equal to 6
const regex = /^(e.{6,}|[^e].{0,5})$/;
function strangeWords(words) {
return words.filter(word => regex.test(word));
}
const regex = /^(e.{6,}|[^e].{0,5})$/;
function strangeWords(words) {
return words.filter(word => regex.test(word));
}
console.log(
strangeWords([
"e123456", // VALID: Starts with `e` and has length 7
"123456", // VALID: Does not start 'e' and has length 6
"123", // VALID: length less then 6
"e123", // INVALID: start with `e` but length less then 6
"1234556d" // INVALID: length greater then 6
])
)
I think this is all you will need I negated the condition for all strings that starts with e and length is less than 6
So basically what is happening is filter checks if string is length <6 and starts with "e" and if it does it does not include it in result
let test = ["abc", "ertyujkhgf", "trfvdfgrwgs", "ertfs"];
out = test.filter(ele=>{return ele[0]==="e" ^ ele.length<6})
console.log(out)
This image is a simple Venn Diagram showing overlap of words starting with e and length < 6
So Mathematically We Want
Set A Set of all words starting with e
Set B Set of all words with length less than 6
Every thing from set A and Set B That is not in A∩B
Which will look like
Everything in white area And this is equal to a XOR operation On A and B
A ^ B
I would make it this way, more readable when you have names for your conditions.
You also did not use your loop index, so I added it.
function strangeWords(words)
{
let result = [];
let isShorterThanSix;
let isBeginWithE;
for(var i = 0; i < words.length; i++) {
isShorterThanSix = words[i].length < 6;
isBeginWithE = words[i][0] === 'e';
if(isShorterThanSix || isBeginWithE){
if(isShorterThanSix && isBeginWithE){
continue;
}
//Add only the needed items to the result
result.push(words[i]);
}
return result;
}
}

How do I reverse a string by character instead of flipping the whole string?

If the string is "hello world",
I need to flip every pair of characters and return "ehll oowlrd".
The way I do it returns "olleh dlrow".
var flipPairs = function (string) {
return string.split("").reverse().join("");
}
console.log(flipPairs("hello world"));
// -> ehll oowlrd
I'd use a regular expression - match two consecutive characters, and replace with them in reversed order:
const flipPairs = str => str.replace(/(.)(.)/g, '$2$1');
console.log(flipPairs("hello world"));
You could calculate the index and take this character of the actual one if the string has an odd length.
function flipPairs(string) {
return [...string].map((c, i, a) => a[i + ((i + 1) % 2 || -1)] || c).join('');
}
console.log(flipPairs('hello world'));
What about this:
function flip(s)
{
let r = "";
for (let i = 0; i < s.length; i+=2)
r += (i+1 < s.length ? s[i+1] : "")+s[i];
return r;
}
console.log(flip("hello world"));

Finding matches using regular expression

How to find the length of consecutive zeros that is surrounded by ones at both ends of a binary?
For example, in 10010001 the 2 matches are 1001 and 10001
1001 the length of zeroes is 2
10001 the length of zeroes is 3
I used match which returned only the last one i.e. 10001.
'1010001'.match(/1(0+)1$/g)
You need lookahead assertions here:
console.log('1010001'.match(/10+(?=1)/g).map(function(x) {
return x.length - 1;
}));
First, replace all '1' by '11', then remove $ - from your regular expression.
console.log('10010001'.replace(/1/g, '11').match(/1(0+)1/g));
In regular expressions, $ is a special character matching the end of the string (MDN).
But that's only half the problem. String#match captured the trailing 1 in the first group and could not create a second overlapping group for '10001'. Try using RegExp#exec instead. The regular expression is stateful, and in this case, you'll want to move the last index back one for each match you find.
var re = /10+1/g;
var str = '10010001';
var matches = [];
var result;
while ((result = re.exec(str)) !== null) {
matches.push(result[0]);
re.lastIndex--;
}
console.log(matches);
Instead of using Regex, I would keep it simple.
Split the string on '1's
Get the length of each section
Remove the first and last section length because they aren't surrounded by '1's
Find the highest section length
const maxNumberOfZeros = Math.max(...'000000101000100000'.split('1').map(str => str.length).slice(1, -1));
console.log(maxNumberOfZeros);
function solution(N) {
let s = (N >>> 0).toString(2).split('');
let max = 0;
//1 1 0 0 1 0 0 0 1 0
if(s.length > 2){
let lastDigit = s[s.length - 1];
let firstDigit = s[0];
while(lastDigit == '0'){
s.pop();
if(!s.length || s.length == 1) return 0;
lastDigit = s[s.length - 1];
}
while(firstDigit == '0'){
s.shift();
if(!s.length || s.length == 1) return 0;
firstDigit = s[0];
}
let x = s.join('').split('1').filter(i => i !== '').sort().reverse();
return x.length ? x[0].length : 0;
}
return 0;
}
The $ within RegExp matches end of string.
You can use Array.prototype.reduce() and logic to evaluate 1 and 0 as a Boolean to determine if the sequence matches the required pattern
let nums = ["1000000001010001", "0000000101", "100000000"];
let countZerosBetweenOnes = str =>
(res => ([...str].reduce((a, b, idx, arr) =>
(!!+a && !+b && arr.find((n, i) => i > idx && !!+n) && res.push(+a)
, !+a && !+b && res.length && ++res[res.length - 1], b))
, res.sort((a, b) => a - b).pop() || 0))([]);
nums.forEach(n => console.log(countZerosBetweenOnes(n)));

How to validate number and capital letter in javascript

I want to validate password :
contain at least 1 number
contain at least 1 capital letter (uppercase)
contain at least 1 normal letter (lowercase)
I used this code
function validate()
{
var a=document.getElementById("pass").value
var b=0
var c=0
var d=0;
for(i=0;i<a.length;i++)
{
if(a[i]==a[i].toUpperCase())
b++;
if(a[i]==a[i].toLowerCase())
c++;
if(!isNaN(a[i]))
d++;
}
if(a=="")
{
alert("Password must be filled")
}
else if(a)
{
alert("Total capital letter "+b)
alert("Total normal letter "+c)
alert("Total number"+d)
}
}
One thing that make me confuse is why if I input a number, it also count as uppercase letter???
Regular expressions are more suitable for this. Consider:
var containsDigits = /[0-9]/.test(password)
var containsUpper = /[A-Z]/.test(password)
var containsLower = /[a-z]/.test(password)
if (containsDigits && containsUpper && containsLower)
....ok
A more compact but less compatible option is to use a boolean aggregate over an array of regexes:
var rules = [/[0-9]/, /[A-Z]/, /[a-z]/]
var passwordOk = rules.every(function(r) { return r.test(password) });
Docs: test, every
"1".toUpperCase == "1" ! What do you say about that :)
You could do your checking like this:
for(i=0;i<a.length;i++)
{
if('A' <= a[i] && a[i] <= 'Z') // check if you have an uppercase
b++;
if('a' <= a[i] && a[i] <= 'z') // check if you have a lowercase
c++;
if('0' <= a[i] && a[i] <= '9') // check if you have a numeric
d++;
}
Now if b, c, or d equals 0, there is a problem.
toUpperCase() and toLowerCase() will still return the character if it's not able to be converted, so your tests will succeed for numbers.
Instead, you should check first that isNaN(a[i]) is true before testing using toLowerCase/toUpperCase.
The very short way could be:
var pwd = document.getElementById("pass").value,
valid = Number(/\d/.test('1abcD'))+
Number(/[a-z]/.test('1abcD'))+
Number(/[A-Z]/.test('1abcD')) === 3;

How to sort strings in JavaScript numerically

I would like to sort an array of strings (in JavaScript) such that groups of digits within the strings are compared as integers not strings. I am not worried about signed or floating point numbers.
For example, the result should be ["a1b3","a9b2","a10b2","a10b11"] not ["a1b3","a10b11","a10b2","a9b2"]
The easiest way to do this seems to be splitting each string on boundaries around groups of digits. Is there a pattern I can pass to String.split to split on character boundaries without removing any characters?
"abc11def22ghi".split(/?/) = ["abc","11","def","22","ghi"];
Or is there another way to compare strings that does not involve splitting them up, perhaps by padding all groups of digits with leading zeros so they are the same length?
"aa1bb" => "aa00000001bb", "aa10bb" => "aa00000010bb"
I am working with arbitrary strings, not strings that have a specific arrangement of digit groups.
I like the /(\d+)/ one liner from Gaby to split the array. How backwards compatible is that?
The solutions that parse the strings once in a way that can be used to rebuild the originals are much more efficient that this compare function. None of the answers handle some strings starting with digits and others not, but that would be easy enough to remedy and was not explicit in the original question.
["a100", "a20", "a3", "a3b", "a3b100", "a3b20", "a3b3", "!!", "~~", "9", "10", "9.5"].sort(function (inA, inB) {
var result = 0;
var a, b, pattern = /(\d+)/;
var as = inA.split(pattern);
var bs = inB.split(pattern);
var index, count = as.length;
if (('' === as[0]) === ('' === bs[0])) {
if (count > bs.length)
count = bs.length;
for (index = 0; index < count && 0 === result; ++index) {
a = as[index]; b = bs[index];
if (index & 1) {
result = a - b;
} else {
result = !(a < b) ? (a > b) ? 1 : 0 : -1;
}
}
if (0 === result)
result = as.length - bs.length;
} else {
result = !(inA < inB) ? (inA > inB) ? 1 : 0 : -1;
}
return result;
}).toString();
Result: "!!,9,9.5,10,a3,a3b,a3b3,a3b20,a3b100,a20,a100,~~"
Another variant is to use an instance of Intl.Collator with the numeric option:
var array = ["a100", "a20", "a3", "a3b", "a3b100", "a3b20", "a3b3", "!!", "~~", "9", "10", "9.5"];
var collator = new Intl.Collator([], {numeric: true});
array.sort((a, b) => collator.compare(a, b));
console.log(array);
I think this does what you want
function sortArray(arr) {
var tempArr = [], n;
for (var i in arr) {
tempArr[i] = arr[i].match(/([^0-9]+)|([0-9]+)/g);
for (var j in tempArr[i]) {
if( ! isNaN(n = parseInt(tempArr[i][j])) ){
tempArr[i][j] = n;
}
}
}
tempArr.sort(function (x, y) {
for (var i in x) {
if (y.length < i || x[i] < y[i]) {
return -1; // x is longer
}
if (x[i] > y[i]) {
return 1;
}
}
return 0;
});
for (var i in tempArr) {
arr[i] = tempArr[i].join('');
}
return arr;
}
alert(
sortArray(["a1b3", "a10b11", "a10b2", "a9b2"]).join(",")
);
Assuming you want to just do a numeric sort by the digits in each array entry (ignoring the non-digits), you can use this:
function sortByDigits(array) {
var re = /\D/g;
array.sort(function(a, b) {
return(parseInt(a.replace(re, ""), 10) - parseInt(b.replace(re, ""), 10));
});
return(array);
}
It uses a custom sort function that removes the digits and converts to a number each time it's asked to do a comparison. You can see it work here: http://jsfiddle.net/jfriend00/t87m2/.
Use this compare function for sorting...
function compareLists(a, b) {
var alist = a.split(/(\d+)/), // Split text on change from anything
// to digit and digit to anything
blist = b.split(/(\d+)/); // Split text on change from anything
// to digit and digit to anything
alist.slice(-1) == '' ? alist.pop() : null; // Remove the last element if empty
blist.slice(-1) == '' ? blist.pop() : null; // Remove the last element if empty
for (var i = 0, len = alist.length; i < len; i++) {
if (alist[i] != blist[i]){ // Find the first non-equal part
if (alist[i].match(/\d/)) // If numeric
{
return +alist[i] - +blist[i]; // Compare as number
} else {
return alist[i].localeCompare(blist[i]); // Compare as string
}
}
}
return true;
}
Syntax
var data = ["a1b3", "a10b11", "b10b2", "a9b2", "a1b20", "a1c4"];
data.sort(compareLists);
alert(data);
There is a demo at http://jsfiddle.net/h9Rqr/7/.
Here's a more complete solution that sorts according to both letters and numbers in the strings
function sort(list) {
var i, l, mi, ml, x;
// copy the original array
list = list.slice(0);
// split the strings, converting numeric (integer) parts to integers
// and leaving letters as strings
for( i = 0, l = list.length; i < l; i++ ) {
list[i] = list[i].match(/(\d+|[a-z]+)/g);
for( mi = 0, ml = list[i].length; mi < ml ; mi++ ) {
x = parseInt(list[i][mi], 10);
list[i][mi] = !!x || x === 0 ? x : list[i][mi];
}
}
// sort deeply, without comparing integers as strings
list = list.sort(function(a, b) {
var i = 0, l = a.length, res = 0;
while( res === 0 && i < l) {
if( a[i] !== b[i] ) {
res = a[i] < b[i] ? -1 : 1;
break;
}
// If you want to ignore the letters, and only sort by numbers
// use this instead:
//
// if( typeof a[i] === "number" && a[i] !== b[i] ) {
// res = a[i] < b[i] ? -1 : 1;
// break;
// }
i++;
}
return res;
});
// glue it together again
for( i = 0, l = list.length; i < l; i++ ) {
list[i] = list[i].join("");
}
return list;
}
I needed a way to take a mixed string and create a string that could be sorted elsewhere, so that numbers sorted numerically and letters alphabetically. Based on answers above I created the following, which pads out all numbers in a way I can understand, wherever they appear in the string.
function padAllNumbers(strIn) {
// Used to create mixed strings that sort numerically as well as non-numerically
var patternDigits = /(\d+)/g; // This recognises digit/non-digit boundaries
var astrIn = strIn.split( patternDigits ); // we create an array of alternating digit/non-digit groups
var result = "";
for (var i=0;i<astrIn.length; i++) {
if (astrIn[i] != "") { // first and last elements can be "" and we don't want these padded out
if (isNaN(astrIn[i])) {
result += astrIn[i];
} else {
result += padOneNumberString("000000000",astrIn[i]);
}
}
}
return result;
}
function padOneNumberString(pad,strNum,left) {
// Pad out a string at left (or right)
if (typeof strNum === "undefined") return pad;
if (typeof left === "undefined") left = true;
var padLen = pad.length - (""+ strNum).length;
var padding = pad.substr(0,padLen);
return left? padding + strNum : strNum + padding;
}
Sorting occurs from left to right unless you create a custom algorithm. Letters or digits are compared digits first and then letters.
However, what you want to accomplish as per your own example (a1, a9, a10) won’t ever happen. That would require you knowing the data beforehand and splitting the string in every possible way before applying the sorting.
One final alternative would be:
a) break each and every string from left to right whenever there is a change from letter to digit and vice versa; &
b) then start the sorting on those groups from right-to-left. That will be a very demanding algorithm. Can be done!
Finally, if you are the generator of the original "text", you should consider NORMALIZING the output where a1 a9 a10 could be outputted as a01 a09 a10. This way you could have full control of the final version of the algorithm.

Categories