I have items in a cms which have a 6-digit number.
The user can filter these item, via a input field,
by start typing a number.
const list = document.querySelector('#filter-wrap');
const searchBar = document.forms['search-kelim'].querySelector('input');
searchBar.addEventListener('keyup', function(e){
const term = e.target.value.toLowerCase();
const kelims = list.getElementsByClassName('filter-item');
Array.from(kelims).forEach(function(kelim){
let number = kelim.firstElementChild.textContent;
if(number.toLowerCase().indexOf(term) != -1 ){
console.log("Valid");
} else {
console.log("Invalid");
}
});
});
This is working, but it filters no matter where the digit
is occurring within the 6-digit number.
Aim is, it should only filter the first 3 starting digits, already starting with the first digit.
Meaning, if the user types 2, only the items starting with 2 are shown,
if the user then types 1, only the items starting with 21 are shown.
(the same for the third digit, typing 214 matches only the items starting with 214)
instead of indexof i tried with regex, but cannot get it to work:
var re = new RegExp("^[0-9]+$");
if (re.test(term)) {
console.log("Valid");
} else {
console.log("Invalid");
}
also tried these regex:
var re = new RegExp("^[0-9]");
var re = new RegExp("^\d{3}[0-9]");
var re = new RegExp("/[0-9]{1}[0-9]{1}[0-9]{1}/");
i also tried with match but also no luck, (different syntax?)
UPDATE:
here are two codepens for better understanding:
Filter with indexof, working but for first 3 digits.
https://codepen.io/hhentschel/pen/LYNWKeK
Filter with Regex, i tried all different answers, which came up so far.
https://codepen.io/hhentschel/pen/yLOMmbw
Your number variables all start with a line break. You may easily check that if you add console.log("'"+number+"': number") in the code.
To fix the regex approach, you just need to trim the incoming strings:
var re = new RegExp("^"+term);
if (re.test(number.trim())) { // <-- HERE!
kelim.classList.add("block");
kelim.classList.remove("hide");
} else {
kelim.classList.add("hide");
kelim.classList.remove("block");
}
Just check whether the Index is 0:
if(number.toLowerCase().indexOf(term) == 0){
console.log("Valid");
} else {
console.log("Invalid");
}
So you know that the term is at the beginning of the number.
But if you want to use regex, you have to build a new pattern every time:
var re = new RegExp("^"+term);
if (re.test(number)) {
console.log("Valid");
} else {
console.log("Invalid");
}
Related
Hello I don't find how to detect an incomplet date from listString. I think about regex but don't know how to extract this sequence input.
input=[2022-01-20 20:01, 2022-01-20 21, 2022-01-20 22:25, 2022-01-20 23:01]
Here I tried to match 2022-01-20 21 (it's the only who not have minute)
after match I want to add minute :00 to remove wrong date format
Here is what I search to have
output=[2022-01-20 20:01, 2022-01-20 21:00, 2022-01-20 22:25, 2022-01-20 23:01]
here is what I tried
dateList=[2022-01-20 20:01, 2022-01-20 21, 2022-01-20 22:25, 2022-01-20 23:01];
for (var i = 1; i < dateList.length; i++) {
RegExp regExp = new RegExp(
r"^((?!:).)*$",
);
var match = regExp.firstMatch("${dateList}");
var index = dateList1.indexOf(match);
dateList.replaceRange(index, index + 1, ["$match:00"]);
}
for each index of my stringlist I seach the only who not have : after I found the index who have a problem, and I replace the index with the add :00
problem match return null...
Thank you
I agree that using regular expressions is the way to go here. Detecting a date is relatively simple, you're basically looking for
4-digits dash 2-digits dash 2-digits space 2-digits colon 2-digits
Which, in RegExp language is
\d{4}-\d{2}-\d{2} \d{2}:\d{2}
Now we can detect whether a given String contains a complete datetime. The only thing that's left is to add the trailing minutes when it is missing. Note that you can decide what to add using another regular expression, but this code will just add the minutes, assuming that's always the issue.
List<String> input = ['2022-01-20 20:01', '2022-01-20 21', '2022-01-20 22:25', '2022-01-20 23:01'];
List<String> output = [];
// detect a date + time
RegExp regex = RegExp(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}');
for (String maybeDate in input) {
bool isCompleteDate = regex.hasMatch(maybeDate);
if (isCompleteDate) {
output.add(maybeDate);
} else {
// we want to comlete the String
// in this case, I assume it's always just the minutes missing, but you could use another regex to see which part is missing
output.add(maybeDate + ':00');
}
}
print(output);
Alternatively, you can indeed use negative lookahead to find the missing minutes:
// detects a date and hour, without a colon and two digits (the minutes)
RegExp missingMinutes = RegExp(r'(\d{4}-\d{2}-\d{2} \d{2})(?!:\d{2})');
Which, in case you have a String instead of a List<String> would result in
List<String> input = ['2022-01-20 20:01', '2022-01-20 21', '2022-01-20 22:25', '2022-01-20 23:01'];
String listAsString = input.toString();
RegExp missingMinutes = RegExp(r'(\d{4}-\d{2}-\d{2} \d{2})(?!:\d{2})');
List<RegExpMatch?> matches = missingMinutes.allMatches(listAsString).toList();
for (int i = matches.length - 1; i >= 0; i--) {
// walk through all matches
if (matches[i] == null) continue;
listAsString = listAsString.substring(0, matches[i]!.end) + ':00' + listAsString.substring(matches[i]!.end);
}
print(listAsString);
I want paragraphs to be up to 3 sentences only.
For that, my strategy is to loop on all paragraphs and find the 3rd sentence ending (see note). And then, to add a "\r" char after it.
This is the code I have:
for (var i = 1; i < paragraphs.length; i++) {
...
sentEnds = paragraphs[i].getText().match(/[a-zA-Z0-9_\u0590-\u05fe][.?!](\s|$)|[.?!][.?!](\s|$)/g);
//this array is used to count sentences in Hebrew/English/digits that end with 1 or more of either ".","?" or "!"
...
if ((sentEnds != null) && (sentEnds.length > 3)) {
lineBreakAnchor = paragraphs[i].getText().match(/.{10}[.?!](\s)/g);
paragraphs[i].replaceText(lineBreakAnchor[2],lineBreakAnchor[2] + "\r");
}
}
This works fine for round 1. But if I run the code again- the text after the inserted "\r" char is not recognized as a new paragraph. Hence, more "\r" (new lines) will be inserted each time the script is running.
How can I make the script "understand" that "\r" means new, separate paragraph?
OR
Is there another character/approach that will do the trick?
Thank you.
Note: I use the last 10 characters of the sentence assuming the match will be unique enough to make only 1 replacement.
Without modifying your own regex expression you can achieve this.
Try this approach to split the paragraphs:
Grab the whole content of the document and create an array of sentences.
Insert paragraphs with up to 3 sentences after original paragraphs.
Remove original paragraphs from hell.
function sentenceMe() {
var doc = DocumentApp.getActiveDocument();
var paragraphs = doc.getBody().getParagraphs();
var sentences = [];
// Split paragraphs into sentences
for (var i = 0; i < paragraphs.length; i++) {
var parText = paragraphs[i].getText();
//Count sentences in Hebrew/English/digits that end with 1 or more of either ".","?" or "!"
var sentEnds = parText.match(/[a-zA-Z0-9_\u0590-\u05fe][.?!](\s|$)|[.?!][.?!](\s|$)/g);
if (sentEnds){
for (var j=0; j< sentEnds.length; j++){
var initIdx = 0;
var sentence = parText.substring(initIdx,parText.indexOf(sentEnds[j])+3);
var parInitIdx = initIdx;
initIdx = parText.indexOf(sentEnds[j])+3;
parText = parText.substring(initIdx - parInitIdx);
sentences.push(sentence);
}
}
// console.log(sentences);
}
inThrees(doc, paragraphs, sentences)
}
function inThrees(doc, paragraphs, sentences) {
// define offset
var offset = paragraphs.length;
// Create paragraphs with up to 3 sentences
var k=0;
do {
var parText = sentences.splice(0,3).join(' ');
doc.getBody().insertParagraph(k + offset , parText.concat('\n'));
k++
}
while (sentences.length > 0)
// Remove paragraphs from hell
for (var i = 0; i < offset; i++){
doc.getBody().removeChild(paragraphs[i]);
}
}
In case you are wondering about the custom menu, here is it:
function onOpen() {
var ui = DocumentApp.getUi();
ui.createMenu('Custom Menu')
.addItem("3's the magic number", 'sentenceMe')
.addToUi();
}
References:
DocumentApp.Body.insertParagraph
Actually the detection of sentences is not an easy task.
A sentence does not always end with a dot, a question mark or an exclamation mark. If the sentence ends with a quote then punctuation rules in some countries force you to put the end of the sentence mark inside the quote:
John asked: "Who's there?"
Not every dot means an end of a sentence, usually the dot after an uppercase letter does not end the sentence, because it occurs after an initial. The sentence does not end after J. here:
The latest Star Wars movie has been directed by J.J. Abrams.
However, sometimes the sentence does end after a capital letter followed by a dot:
This project has been sponsored by NASA.
And abbreviations can make it very hard:
For more information check the article in Phys. Rev. Letters 66, 2697, 2013.
Having in mind these difficulties let's still try to get some expression which will work in "usual" cases.
Make a global match and substitution. Match
((?:[^.?!]+[.?!] +){3})
and substitute it with
\1\r
Demo
This looks for 3 sentences (a sentence is a sequence of not-dot, not-?, not-! characters followed by a dot, a ? or a ! and some spaces) and puts a \r after them.
UPDATED 2020-03-04
Try this:
var regex = new RegExp('((?:[a-zA-Z0-9_\\u0590-\\u05fe\\s]+[.?!]+\\s+){3})', 'gi');
for (var i = 1; i < paragraphs.length; i++) {
paragraphs[i].replaceText(regex, '$1\\r');
}
I have a form that asks for a password and I want to validate if the password has at least eight characters, and of these eight characters at least two must be numbers and two must be letters in any order. I'm trying with this:
function validatePassword():void
{
var passVal:String = pass.text;
if(validPass(passVal))
{
trace("Password Ok");
sendForm();
}
else
{
trace("You have entered an invalid password");
}
function validPass(passVal:String):Boolean{
var pw:RegExp = /^?=.{8,}[A-Za-z]{2,}[0-9]{2,}/;
return(pw.test(passVal));
}
}
But it doesn't work. What I'm doing wrong?
Any help would be really appreciated!
use this pattern ^(?=.{8})(?=(.*\d){2})(?=(.*[A-Za-z]){2}).*$
^ anchor
(?=.{8}) look ahead for at least 8 characters
(?=(.*\d){2}) look ahead for at least 2 digits in any order
(?=(.*[A-Za-z]){2}) look ahead for at least 2 letters in any order
.*$ catch everything to the end if passed previous conditions
The problem is that your regex is forcing the numbers to follow the letters ([A-Za-z]{2,}[0-9]{2,}). While it is possible to write such a regex, I suggest using a simple length check and two regexes:
function validPass(passVal:String):Boolean{
if (passVal.length < 8)
return False;
var letterRegex:RegExp = /^.*?[A-Za-z].*?[A-Za-z].*?$/;
var numberRegex:RegExp = /^.*?\d.*?\d.*?$/;
return letterRegex.test(passVal) && numberRegex.test(passVal);
}
What is the regex needed to remove all except second+ occurrence of the duplicate entries?
data sets are separated by commas.
Example: This needs to convert to
#20131229PV1,#20140109PV5,#20140101PV1,#20140109PV5,#20140109PV5,#20131224PV5,
This
#20140109PV5,#20140109PV5,
after going through regex
Unfortunately, there is no way to find duplicate string sets using regex. You need a good string-based algorithm and implement it in your favorite computer language to achieve this.
Answered through a different approach (Javascript)
function find_duplicates(arr) {
var len=arr.length,
out=[],
counts={};
for (var i=0;i<len;i++) {
var item = arr[i];
var count = counts[item];
counts[item] = counts[item] >= 1 ? counts[item] + 1 : 1;
}
for (var item in counts) {
if(counts[item] > 1)
out.push(item);
}
alert(out);
}
find_duplicates(bookcaldatesci.innerHTML.split(","));
my regex query below does an exact match of a word say Bob or Bill for example
var regExp = new RegExp("^" + inputVal + "$", 'i');
what i want it to do is match anything exactly (Bob or Bill Etc) but not match Fred
so match anything exactly except for Fred, does that make sense?
anyone help me out as to how i do that?
Thanks
EDIT 2:
i thought id show my actual script instead, what im doing is searching a table, and im page load i want to hide rows that contain a string. so if exlucde lenght is greater than 0 hide that row...
function searchPagingTable(inputVal, tablename, fixedsearch, exclude) {
var table = $(tablename);
table.find('tr:not(.header)').each(function (index, row) {
var allCells = $(row).find('td');
if (allCells.length > 0) {
var found = false;
allCells.each(function (index, td) {
if (fixedsearch == 1) {
var regExp = new RegExp("^" + inputVal + "$", 'i');
}
else if (exclude.length > 0)
{
var regExp = new RegExp("^(?!" + exclude + ")", "i");
}
else {
var regExp = new RegExp(inputVal, 'i');
}
if (regExp.test($(td).text())) {
found = true;
return false;
}
});
if (found == true) $(row).show().removeClass('exclude'); else $(row).hide().addClass('exclude');
}
});
pa
ginate();
}
That would be
var exclude = "Fred"
var regExp = new RegExp("^(?!.*" + exclude + ")", "i");
This regex matches any string except those that contain Fred. It doesn't actually match any characters in the string, but that's sufficient if you're just looking for a true/false result.
This will also find strings that contain Alfred or Fredo, so if you don't want that, you need to tell the regex only to look for entire words using word boundaries:
var regExp = new RegExp("^(?!.*\\b" + exclude + "\\b)", "i");
You need to make sure that your exclude string only contains ASCII letters/digits (or underscores) for this to work correctly.
You could populate a list of names you wish to match against:
var validNames = ['bob', 'bill'];
Then lowercase each input and match against the list:
if (validNames.indexOf(inputVal.toLowerCase()) != -1) {
// it's a good name
}
For older browsers you have to shim Array.indexOf()
var re = new RegExp('^\\s*Fred\\s*$','i');
if (inputVal.match(re)) {
// Fred has been found
} else {
// Anything has been found
}