How to apply Multiple ng-pattern with one input control - angularjs
I am trying to Validate Postal code and Phone Number in Text box using Angularjs. But it's not working
<input type="text" class="form-control errorfields" id="postalCode"
name="postalCode" ng-model="postalCode"
ng-pattern="/(^(\d{5}(-\d{4})?|[A-CEGHJ-NPRSTVXY]\d[A-CEGHJ-NPRSTV-Z]
?\d[A-CEGHJ-NPRSTV-Z]\d)$)||(^[0-9])/" required>
See this answer:
Angularjs dynamic ng-pattern validation
You can add a function from scope that returns a true or false based on the validation test. Here is some code below to check out. See the answer for additional information:
Controller:
$scope.postalCodeValidation = (function() {
var regexp = /^\(?(\d{3})\)?[ .-]?(\d{3})[ .-]?(\d{4})$/;
return {
test: function(value) {
return regexp.test(value);
}
};
})();
HTML:
<input type="text" class="form-control errorfields" id="postalCode"
name="postalCode" ng-model="postalCode"
ng-pattern="postalCodeValidation " required>
First, having double pipes in your regex syntax as mico pointed out, is invalid and will break your expression.
Second, ng-pattern can only validate one pattern per input. If you need it to validate either or, you will need to go one of two routes, create a custom directive, or add some logic to the controller to determine which expression we should check against and pass it into ng-pattern using data binding. This is bad practice in the angular world, so your best bet is to make a directive.
Edit: This will only work for regular expressions with starting ^ and ending $ metacharacters.
Like many of my StackOverflow answers, I'm super late to this party. This question got me on the right track, but none of the answers given were good enough to make a final solution.
Here's something I wrote recently after reading this thread which didn't fully answer my question, which made my code reviewer exclaim, "WTF IS THIS?!" Followed by him hitting the MERGE button.
My problem was I had a form input which I wanted to accept and validate against latitude or longitude regular expressions. I wanted to match against DMS, DDM, and DD style inputs. I could have written one giant expression, but I also needed to test the input at the component level to determine which type the user input and convert it to DD. The expressions for lat/long would make this answer even more complicated, so I am using what I think the original author intended.
If you're interested in lat/long expressions, here's a gist: https://gist.github.com/pjobson/8f44ea79d1852900457bc257a4c9fcd5
Here's kind of a rewrite of my code for this exercise, I abstracted it from angular to make it more accessible as it could be used for other purposes.
It looks like you're accepting: North America Zip Code OR UK Zip Code OR something which starts with 0-9.
Fleshing out the regex a bit, I haven't tested these myself very well, so your mileage may vary. This is what the need seemed to look like to me, it is just sample code, so it doesn't really matter that much 3+ years later.
N.America Zip or Zip+4 (from sample above)
^(\d{5}(-\d{4})?$
UK Zip Code (from wikipedia)
^([A-Za-z][A-Ha-hJ-Yj-y]?[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}|[Gg][Ii][Rr] ?0[Aa]{2})$
N.America Phone Number (from RegExLib.com)
^[2-9]\d{2}-\d{3}-\d{4}$
UK Phone Number (from RegExLib.com)
^(\s*\(?0\d{4}\)?\s*\d{6}\s*)|(\s*\(?0\d{3}\)?\s*\d{3}\s*\d{4}\s*)$
In an object...
const expressions = {
naZip: /^\d{5}(-\d{4})?$/,
ukZip: /^([A-Za-z][A-Ha-hJ-Yj-y]?[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}|[Gg][Ii][Rr] ?0[Aa]{2})$/,
naPhone: /^[2-9]\d{2}-\d{3}-\d{4}$/,
ukPhone: /^(\s*\(?0\d{4}\)?\s*\d{6}\s*)|(\s*\(?0\d{3}\)?\s*\d{3}\s*\d{4}\s*)$/
};
Now the problem is ng-pattern ONLY accepts one regex, but I have four. I want to keep those as four different things so in my component I can test to figure out which one the user input and do different actions based on whatever it is.
This looks like garbage, but it works really well.
const combinedExpression = new RegExp(`^(${_.map(expressions, exp => `(${exp.toString().replace(/^\/\^(.+?)\$\/$/,'$1')})`).join('|')})$`);
From here of course you can add this to your model and set your ng-pattern to it.
I'll try to explain this in several different ways here...
Here is a break down of what it is doing.
new RegExp(`^(${_.map(expressions, exp => `(${exp.toString().replace(/^\/\^(.+?)\$\//g,'$1')})`).join('|')})$`);
^ ^^^^ ^ ^ ^ ^ ^ ^ ^ ^ ^^
| |||| | | | | | | | | ||
| |||| | | | | | | | | |+- end of the 1st template literal
| |||| | | | | | | | | +- end the expression with $
| |||| | | | | | | | +- join all stringified expressions with a pipe so (a|b|c|d)
| |||| | | | | | | +- end of 2nd template literal
| |||| | | | | | +- replace the like /^(\d{5}(-\d{4})?$/ to (\d{5}(-\d{4})?
| |||| | | | | +- convert the expression to a string
| |||| | | | +- new Expression interpolation
| |||| | | +- 2nd templte literal
| |||| | +- each expression is represented by exp
| |||| +- Using lodash map the expressions
| |||+- Expression interpolation
| ||+- Regex group match, we will be grouping each of the expressions
| |+- Regex starts with ^
| +- Starting a new template literal
+- Establish a new combined expression.
Further clarification...
What I'm doing is taking each of the expressions, converting them to strings, removing the starting end ending characters, joining them back together as an OR Group and then converting the whole thing to one huge combined regular expression.
Further clarification...
If that was too confusing, which I can see how it may be...
Loop through each of the expressions.
Convert each of those values to a string removing the starting and ending components so /^(\d{5}(-\d{4})?$/ converts to (\d{5}(-\d{4})?.
Join all of those together into one big OR Group so you'd get something like (a|b|c|d) where a, b, c, d are each of your stringified expressions.
Wrap the OR Group with starts with ^ and ends with $.
Wrap the entire thing with in a new RegExp.
Further clarification, in long hand JS.
// Instantiate a new empty array
const strings = [];
// Loop the expressions
for (let key in expressions) {
// Console log to see what we're getting
console.log('original expression', expressions[key]);
// assign the current expression
let stringifiedExp = expressions[key];
// convert the current expression to a string
stringifiedExp = stringifiedExp.toString();
// replace the starting /^ and ending $/ with the contents
stringifiedExp = stringifiedExp.replace(/^\/\^(.+?)\$\/$/,'$1');
// Console log to see what we got.
console.log('stringified expression', stringifiedExp);
// Push the stringified expression to strings.
strings.push(stringifiedExp);
}
// Instantiate a new string which we'll build the regex with
let expString = '^(';
expString += strings.join('|');
expString += ')$';
// Console log to see what we've got.
console.log(expString);
// Make a new Regular Expression out of the string
const combinedExpression = new RegExp(expString);
// Console log what we've got
console.log(combinedExpression);
Your regex has two || instead of single, which breaks the regex validation clause.
I pasted it to (https://regex101.com) and it says that char breaks it.
I think you need to wrap a ng-form around your input. And then you can use
[formName].postalCode.$error
Related
How to validate two data sets coming from an algorithm to check its effectiveness
I have two data sets: Ist (AKA "OLD") [smaller - just a sample]: Origin | Alg.Result | Score Star123 | Star123 | 100 Star234 | Star200 | 90 Star421 | Star420 | 98 Star578 | Star570 | 95 ... | ... | ... IInd (AKA "NEW") [bigger - used all real data]: Origin | Alg.Result | Score Star123 | Star120 | 90 Star234 | Star234 | 100 Star421 | Star423 | 98 Star578 | Star570 | 95 ... | ... | ... Those DFs are the results of two different algorithms. Let's call them "OLD" and "NEW". The logic of those algorithms is following: it takes value from some table (represented in the column: 'Origin'), and tries to match this value from some different table (outcome represented as a column: Alg. Result). Plus it calculates a score of the match based on some internal logic (column: Score). Additionally important information: I DF (old) is a smaller sample II DF (new) is a bigger sample Values in ORIGIN are the same for both datasets, excluding the fact that the old dataset has fewer of them compared to the NEW set. Values in Alg. Result can: be exactly the same as in Origin can be similar can be completely something else In a solution where those algorithms are used, the threshold is used based on SCORE. For OLD it's a Score > 90. For the new, it's the same. What I want to achieve is to: How accurate is the new algorithm? Validate how accurate is the new approach ("NEW") in matching values with Origin values. What are the discrepancies between the OLD and NEW sets: which cases the OLD has that the NEW doesn't have which cases the NEW has, which the OLD doesn't have What kind of comparison would you do to achieve those goals? I thought about checking: True positive => by taking NEW dataset with condition NEW.Origin == NEW.Alg.Result and NEW.Score == 100 False positive => by taking NEW dataset with condition NEW.Origin != NEW.Alg.Result and NEW.Score == 100 False-negative => by taking NEW dataset with condition NEW.Origin == NEW.Al.Result and NEW.Score != 100 I don't see a sense to count True negatives if the algorithm always generates some match. I'm not sure what this could look like. What else you'd suggest? What to do to compare OLD and NEW values? Do you have some ideas?
Using cells as output table of while loop in octave
So I'm implementing a while loop in my code that just does some simple calculations. The thing is, that I want to have an output that no only shows the final values but all of them from each step. The best I could do was using cell arrays with the following code: i=1; p=(a+b)/2; valores=cell(n, 3); while (i<=n && f(p)!=0); if f(a)*f(p)<0; a=a; b=p; else a=p; b=b; endif i=i+1; p=(a+b)/2; valores(i, :)={i-1 p f(p)}; fprintf('%d %d %d \n', valores{i, :}); endwhile An example output would be: 1 1.25 -1.40998 2 1.125 -0.60908 3 1.0625 -0.266982 4 1.03125 -0.111148 5 1.01562 -0.0370029 But I have two main issues with this method, the first one is that I couldn't find a way to get some text as title in the first line, so I have to explain what each column in a sentence later, and second I don't know how to make it so that all the columns stay at the same distance from each other instead of each text staying at the same distance. I assume this last issue has something to do with the way I used the fprintf line since I'm not to familiar with it. In case it helps to understand what I want to get from this algorithm, I'm trying to calculate the root of a function with the bisection method. And sorry if this was to long or unclear, feel free to give me advise, I'm kinda new here :)
An open-source package called Tablicious can take care of cell, row, and column alignment. Using print statements and whitespace gets tedious and leads to unmaintainable code. Tablicious is a package for GNU Octave that provides relational data structures for Octave. It includes implementations of table arrays, datetime, string, categorical, and some other related stuff. You can think of it as “pandas for Octave”. Installation pkg install https://github.com/apjanke/octave-tablicious/releases/download/v0.3.6/tablicious-0.3.6.tar.gz Example pkg load tablicious Forename = {"Tom"; "Dick"; "Harry"}; Age = [21; 63; 38]; Salary = {"$1"; "$2"; "$3"}; tab = table(Forename, Age, Salary); prettyprint (tab) Result ------------------------------- | Forename | Age | Salary | ------------------------------- | Tom | 21 | $1 | | Dick | 63 | $2 | | Harry | 38 | $3 | ------------------------------- Documentation can be found here.
Google Sheets REGEXEXTRACT from Instagram
I'm facing a particular challenge with regular expression. I am populating a Google sheet with Google search results of Instagram post captions with the following repetitive pattern: A | B | C | D -------------------------------------------------------------------------------- 1 | 10.7k Likes, 1.7k Comments - #kristiannairn on Instagram... | | 2 | 4219 Likes, 176 Comments - #djiglobal on Instagram... | | 3 | 1.1m Likes, 209k Comments - #kristiannairn on Instagram... | | I'm trying unsuccessfully to find the right REGEXEXTRACT formula to extract the number of Likes with decimals and the k/m designators following it and without when it is not existent, to populate Column C, and then the REGEXEXTRACT formula to extract the number of Comments with decimals and the k/m designators following it and without when it is not existent, to populate Column D. So far I was able to come up with this formula for Column C to extract the Likes: =REGEXEXTRACT(B1,"(\.?\d*)\W?(?:Likes)") However, it does not recognize decimals and does not fetch the k/m designators. I have the same problem with the Column D comments formula I found: =REGEXEXTRACT(B1,"(\.?\d*)\W?(?:Comments)") Same here... it does not recognize decimals and does not fetch the k/m designators.
all you need is: =ARRAYFORMULA(IFNA({ REGEXEXTRACT(B2:B, "(.*) Like"), REGEXEXTRACT(B2:B, ", (.*) Comm")}))
For Likes, you may use (\d+(?:\.\d+)?[km]?)\W*Likes\b and for Comments, (\d+(?:\.\d+)?[km]?)\W*Comments\b See the regex demo #1 and regex demo #2. Proof: Details (\d+(?:\.\d+)?[km]?) - Group 1: 1+ digits followed with an optional sequence of . and 1+ digits and then an optional k or m \W* - 0+ non-word chars Likes - a word Likes \b - a word boundary.
Excel Lookup IP addresses in multiple ranges
I am trying to find a formula for column A that will check an IP address in column B and find if it falls into a range (or between) 2 addresses in two other columns C and D. E.G. A B C D +---------+-------------+-------------+------------+ | valid? | address | start | end | +---------+-------------+-------------+------------+ | yes | 10.1.1.5 | 10.1.1.0 | 10.1.1.31 | | Yes | 10.1.3.13 | 10.1.2.16 | 10.1.2.31 | | no | 10.1.2.7 | 10.1.1.128 | 10.1.1.223 | | no | 10.1.1.62 | 10.1.3.0 | 10.1.3.127 | | yes | 10.1.1.9 | 10.1.4.0 | 10.1.4.255 | | no | 10.1.1.50 | … | … | | yes | 10.1.1.200 | | | +---------+-------------+-------------+------------+ This is supposed to represent an Excel table with 4 columns a heading and 7 rows as an example. I can do a lateral check with =IF(AND((B3>C3),(B3 < D3)),"yes","no") which only checks 1 address against the range next to it. I need something that will check the 1 IP address against all of the ranges. i.e. rows 1 to 100. This is checking access list rules against routes to see if I can eliminate redundant rules... but has other uses if I can get it going. To make it extra special I can not use VBA macros to get it done. I'm thinking some kind of index match to look it up in an array but not sure how to apply it. I don't know if it can even be done. Good luck.
Ok, so I've been tracking this problem since my initial comment, but have not taken the time to answer because just like Lana B: I like a good puzzle, but it's not a good use of time if i have to keep guessing +1 to Lana for her patience and effort on this question. However, IP addressing is something I deal with regularly, so I decided to tackle this one for my own benefit. Also, no offense, but getting the MIN of the start and the MAX of the end is wrong. This will not account for gaps in the IP white-list. As I mentioned, this required 15 helper columns and my result is simply 1 or 0 corresponding to In or Out respectively. Here is a screenshot (with formulas shown below each column): The formulas in F2:J2 are: =NUMBERVALUE(MID(B2,1,FIND(".",B2)-1)) =NUMBERVALUE(MID(B2,FIND(".",B2)+1,FIND(".",B2,FIND(".",B2)+1)-1-FIND(".",B2))) =NUMBERVALUE(MID(B2,FIND(".",B2,FIND(".",B2)+1)+1,FIND(".",B2,FIND(".",B2,FIND(".",B2)+1)+1)-1-FIND(".",B2,FIND(".",B2)+1))) =NUMBERVALUE(MID(B2,FIND(".",B2,FIND(".",B2,FIND(".",B2)+1)+1)+1,LEN(B2))) =F2*256^3+G2*256^2+H2*256+I2 Yes, I used formulas instead of "Text to Columns" to automate the process of adding more information to a "living" worksheet. The formulas in L2:P2 are the same, but replace B2 with C2. The formulas in R2:V2 are also the same, but replace B2 with D2. The formula for X2 is =SUMPRODUCT(--($P$2:$P$8<=J2)*--($V$2:$V$8>=J2)) I also copied your original "valid" set in column A, which you'll see matches my result.
You will need helper columns. Organise your data as outlined in the picture. Split address, start and end into columns by comma (ribbon menu Data=>Text To Columns). Above the start/end parts, calculate MIN FOR START, and MAX FOR END for all split text parts (i.e. MIN(K5:K1000) . FORMULAS: VALIDITY formula - copy into cell D5, and drag down: =IF(AND(B6>$I$1,B6<$O$1),"In", IF(OR(B6<$I$1,B6>$O$1),"Out", IF(B6=$I$1, IF(C6<$J$1, "Out", IF( C6>$J$1, "In", IF( D6<$K$1, "Out", IF( D6>$K$1, "In", IF(E6>=$L$1, "In", "Out"))))), IF(B6=$O$1, IF(C6>$P$1, "Out", IF( C6<$P$1, "In", IF( D6>$Q$1, "Out", IF( D6<$Q$1, "In", IF(E6<=$R$1, "In", "Out") )))) ) )))
ms SSRS 2008 R2: Display Computed Values #Error
The Report will display Two Columns Foo and Bar Some Rows of Foo are Empty Some have a numerical Value: Foo: +----+ | | +----+ |10.0| +----+ then There is the Bar Column, this column will take the Values from Foo and add 10 to them, the report should yield Results like this: Foo: Bar: +----+----+ | | | +----+----+ |10.0|20.0| +----+----+ Thats the Expression i use to determine whether Foo is numeric inside Bar: =IsNumeric(ReportItems!FooBox.Value) And this is the Result that Expression will Yield: Foo: Bar: +----+-----+ | |False| +----+-----+ |10.0|True | +----+-----+ Okay, thats exactly the way i want it so far, thus i write My Expression that way: =IIf(IsNumeric(ReportItems!FooBox.Value), ReportItems!FooBox.Value +10, "") This will Yield the Following Results: Foo: Bar: +----+------+ | |#Error| +----+------+ |10.0|20.0 | +----+------+ And most Bizare, when i remove the little addition in the Truepart of the IIf, it will execute: =IIf(IsNumeric(ReportItems!FooBox.Value), ReportItems!FooBox.Value, "") Foo: Bar: +----+------+ | | | +----+------+ |10.0|10.0 | +----+------+ It's almost as if the "wrong" part of the Ternary operator gets executed Anyway, thus generating this castError or whatever it might be. How can i convert the Values properly in order to Show the Results as Explained before?
Indeed - using IIf will cause both statements (true and false) to be evaulated and that leads to the error you are getting. being in your situation, I would create my own function to handle this - you can place it in Code field of your Report (click outside of Page area to access Report object) your function might look like this: Public Function MyAdd(val As Object) As Nullable(Of Integer) If TypeOf(val) Is Integer Then Return val + 10 Else Return Nothing End If End Function you might need to play with types used (Object and Nullable Of Integer are maybe not what will work for you) then, of course, use MyAdd in your expression and pass ReportItems!FooBox.Value as parameter