I am trying to write a regular expression which has to adhere the following rules:
Y120.001/100.232k
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
| | | | | | | | This k may be any letter, is not required
| | | | | | | Another number if there was a dot
| | | | | | A dot, not required
| | | | | A number with at least one digit, not required if a letter follows
| | | | Always a slash
| | | If there's a dot, digits can follow
| | A dot, not required
| A number, at least one digit
Always Y
These strings should work:
Y120.001/1k
Y1/h
Y2039/100
Y29/47.0
These should not work:
x203/493s (Not a Y at the start)
Y/39x (No number after the Y)
Y83839 (Second half missing)
Y78/29 (Last letter missing)
This is my early attempt, but it does not work in PHP:
/^\$*(\d)*(.*(\d))\/*(.*(\d))*.$
This pattern should work:
^Y\d+(\.\d*)?/(\d+(\.\d*)?[a-z]?|[a-z])$
Demonstration
In javascript
re=/^Y\d+(\.\d+)?\/([a-z]$|\d+(\.\d+)?[a-z]?$)/
"Y120.001/1k Y1/h Y2039/100 Y29/47.0".split(" ").map(function(s) { return re.test(s) })
> [true, true, true, true]
"x203/493s Y/39x Y83839 Y78/29".split(" ").map(function(s) { return re.test(s) })
> [false, false, false, true]
This does accept Y78/29 as the trailing letter is optional.
After several iterations to correct for commented shortcomings:
/^Y\d+(?:\.\d+)?\/(?:(?:\d+(?:\.\d+)?)[A-Za-z]?|[A-Za-z])$/
Demonstration
Old Answer:
Here is a fully specific version that works well.
/^Y\d+(?:\.\d+)?\/(\d+(?:\.\d+)?)?[A-Za-z]?$/
Edited for a follow-up test in jsfiddle
^Y\d+\.?\d*/((\d+\.?\d*)[a-zA-Z]?|(\d+\.?\d*)?[a-zA-Z])$
You can check the explanation of the regex from here.
If I understand the nuances of your specification
\bY\d+(?:\.\d*)?/(?:[A-Za-z]|(?:\d+(?:\.\d+)?[A-Za-z]?))\b
I understand your specification to include that if the first dot is present, the following number(s) is/are optional; but if the 2nd dot is present, there must be at least one following number. Others seem to have interpreted that part of your requirements differently.
this pattern should work Y[\d.]+\/[\d.]*[a-z]? Demo
What about the following one?
var r = /^Y\d+(\.\d+)?\/(\d+(\.\d+)?|(\d+(\.\d+)?)?[A-Za-z])$/;
console.log(true === r.test('Y120.001/1k')); // true
console.log(true === r.test('Y1/h')); // true
console.log(true === r.test('Y2039/100')); // true
console.log(true === r.test('Y29/47.0')); // true
console.log(false === r.test('x203/493s')); // true
console.log(false === r.test('Y/39x')); // true
console.log(false === r.test('Y83839')); // true
try this:
/Y\d+\.?\d*\/\d+\.?\d*\w?/
Related
I have this quite long string that contains multiple information. So I guess we can say that its a couple of fields concatenated together without any delimiters. I understand that to make this work, all of the lengths of the fields should always be fixed. However, two of the fields represent a name and an amount and no prefixes/suffixes were implemented to maintain a fixed length.
I was wondering how would I got about this problem? Here's a sample of the string and how they should be separated:
Sample #1
Actual Input:
48001MCAbastillas2200800046300017100518110555130000123
How it should be separated:
480 | 01 | MCAbastillas | 2200800046300017 | 100518 | 110555 | 130000 | 123
Sample #2
Actual Input:
48004MCAbastillas22008000463000171005181105555000000123
How it should be separated:
480 | 04 | MCAbastillas | 2200800046300017 | 100518 | 110555 | 5000000 | 123
In my example only the amount has changed but I'm expecting that the name will vary in length as well. Any suggestion will be much appreciated.
I'd probably use a regular expression for this.
String test = "48004MCAbastillas22008000463000171005181105555000000123";
Pattern pattern = Pattern.compile("^(\\d{3})(\\d{2})([A-Za-z]+)(\\d{16})(\\d{6})(\\d{6})(\\d+)(\\d{3})$");
Matcher matcher = pattern.matcher(test);
if (matcher.matches())
{
for (int i = 1; i <= matcher.groupCount(); ++i)
{
System.out.print(matcher.group(i) + " | ");
}
}
Sample output:
480 | 04 | MCAbastillas | 2200800046300017 | 100518 | 110555 | 5000000
| 123 |
Note that the third and second to last groups do not have fixed lengths.
It's more difficult if the name can contain numbers. My approach would be to run this against the data that you have and print a list of anything that doesn't match (i.e. add an else clause). Perhaps then you can come up with a better strategy for handling these cases. For example, something like ([A-Za-z]+\w*[A-Za-z]+) might be an improvement, because that will at least allow numbers in the middle of the name.
Sometimes you just have to accept that when the data you're given is crap, you just have to do the best that you can and that might mean throwing some of it away.
We want the output
480 | 01 | MCAbastillas | 2200800046300017 | 100518 | 110555 | 130000
| 123
where the fields 3 and 7 had no fixed length.
Suppose that we store the string in a string var:
String s="48001MCAbastillas2200800046300017100518110555130000123";
We can find the fields 1 & 2 easly:
System.out.println(s.substring(0, 3)); //has 3 digit
System.out.println(s.substring(3, 5)); //has 2 digit
//we can reduce s
s=s.substring(6); //remove char from 0 to 5 included
If you'll call System.out.println(s); you well see
CAbastillas2200800046300017100518110555130000123
Now we have the string... I can deduce that it is composed only by char. So we have to find the first occurrence of a number... We can use a cycle:
int index=-1;
for( int i=0; i<s.length(); i++ ) {
if( Character.isDigit(s.charAt(i))) {
index=i;
System.out.println("There is a number in the position "+ index);
break;
}
}
Now you can extract your name with:
System.out.println(s.substring(0, index));
and extract the other 3 fields (you can optimize this part...)
System.out.println(s.substring(0, 16));
s=s.substring(16);
System.out.println(s.substring(0, 6));
s=s.substring(6);
System.out.println(s.substring(0, 6));
s=s.substring(6);
Finally, you can divide the remaining s in two part with length s.length.3 and 3:
System.out.println(s.substring(0, s.length()-3));
System.out.println(s.substring( s.length()-3,s.length()));
Your output will be:
480
01
There is a number in the position 11
CAbastillas
2200800046300017
100518
110555
130000
123
I was practicing my javascript with CodeFights and after I finished an exercise I saw this function as a result:
// Subject :
// Several people are standing in a row and need to be divided into two teams.
// The first person goes into team 1, the second goes into team 2,
// the third goes into team 1 again, the fourth into team 2, and so on.
// You are given an array of positive integers - the weights of the people.
// Return an array of two integers, where the first element is the total weight of
// team 1, and the second element is the total weight of team 2
// after the division is complete.
// Example :
// For a = [50, 60, 60, 45, 70], the output should be
// alternatingSums(a) = [180, 105].
// answer
alternatingSums = a => a.reduce((p,v,i) => (p[i&1]+=v,p), [0,0])
I don't understand what p[i&1]+=v,p means.
The & symbol is a bitwise binary operator.
To understand what would happen, you have to convert each item to binary.
| i (decimal) | i (binary) | i & 1 |
|-------------|------------|-------|
| 0 | 0 | 0 |
| 1 | 1 | 1 |
| 2 | 10 | 0 |
| 3 | 11 | 1 |
| 4 | 100 | 0 |
| 5 | 101 | 1 |
Effectively, every even number will be transformed to 0, and every odd number will be transformed to 1.
If I was trying to achieve that outcome, I personally would have used the modulus operator (%)
p[i%2] += v;
But that's just me.
The other part is that there are two statements separated by a comma:
(p[i&1]+=v,p)
That's saying "Perform this action, then return p. It's shorthand for:
alternatingSums = a => a.reduce((p,v,i) => {
p[i&1]+=v;
return p;
},
[0,0])
It looks for an element of the p array that has index of i&1 - it is a bitwise AND operation. Then, increments its value by a value of v variable. Finally, returns the value of p variable.
My question is very brief. I am new to TypeScript, been searching around here and there but didn't find yet an answer.
Does any experienced TypeScripter know if there is a character Type or an easy way to achieve one?
TypeScript does not have a type for representing fixed-length strings.
I'm not sure about easy, but you could sorta do something with string literal types:
type Char = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k'
| 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x'
| 'y' | 'z' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K'
| 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X'
| 'Y' | 'Z' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' // etc....;
of course, this is a fairly brittle solution and breaks down when you consider unicode characters, and I wouldn't really suggest it. As Ryan mentions, JavaScript itself doesn't have any notion of a fixed length string, nor the concept of a char as distinct from a string.
You could use a regex within TypeGuard to contain it's type eg:(you can declare an empty enum to get a new type to be associated with the type guard)
enum CharType { }
export type Char = string & CharType
const isChar = (str: string): str is Char => /^(.|\n)$/.test(
str
)
export function toChar(c: string): Char {
//you can also use is char here for to test whether actually is char
if (!isChar(c)) {
throw new Error('not a char')
}
return c
}
Now Char matches only things that come from calling char(eg actually casted by calling the function rather than just asserted on build time. The compiler simply accepts that a Char is returned and that's actually true if you think of it since it will just throw otherwise)
Original Source(applied to date string):
Atomic Object
Assumptions:
I assume that by mentioning typescript you mean a type used for compile-time checks by typescript compiler and not looking for any kind of optimization on the actual compiled js side(since js only has strings)
The only issue I can see is that you can pass whatever to char function and it will only throw at run time. But you will never reach to a state where you expect a Char and you get something else(since Chars only come from calling char).
On a side note even java casts throw just runtime exceptions.
Although the above approach might not have much to do with casts, I do find some commonalities...
In some comments and answers the lack of a small type of size 8-bit in JavaScript was pointed out. Well, thats not entirely true. With TypedArrays we can create a type of size similar to a classical C-like char. Here a quick take by utilizing Uint8ClampedArray.
type CharString = '!'|'"'|'#'|'$'|'%'|'&'|"'"|'('|')'|'*'|'+'|','|'-'|'.'|'/'|'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|':'|';'|'<'|'='|'>'|'?'|'#'|'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'|'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z'|'['|'\\'|']'|'^'|'_'|'`'|'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j'|'k'|'l'|'m'|'n'|'o'|'p'|'q'|'r'|'s'|'t'|'u'|'v'|'w'|'x'|'y'|'z'|'{'|'|'|'}'|'~';
type CharNumber = 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255;
class Char extends Uint8ClampedArray {
constructor(char: CharNumber | CharString | Char) {
super(1);
this.setValue(char);
}
get _value(): CharNumber {
return this[0] as CharNumber;
}
set _value(char: CharNumber | CharString | Char) {
if (char instanceof Char) {
this[0] = char._value;
} else if (typeof char === 'number') {
this[0] = char;
} else if (typeof char === 'string') {
this[0] = char.charCodeAt(0);
}
}
setValue(char: CharNumber | CharString | Char) {
this._value = char;
}
toString(): CharString {
return String.fromCharCode(this._value) as CharString;
}
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return this.toNumber();
}
return this.toString();
}
toNumber(): CharNumber {
return this._value;
}
}
function char(c: CharNumber | CharString | Char) {
return new Char(c);
}
const messageInChars = [
char(72),
char(101),
char(108),
char(108),
char(111),
char(32),
char(83),
char(116),
char(97),
char(99),
char(107),
char(79),
char(118),
char(101),
char(114),
char(102),
char(108),
char(111),
char(119),
];
console.log('char array as string:', messageInChars.join(''));
console.log('calculating with chars:', +messageInChars[0] + +messageInChars[1]);
Thoughts:
In the example, I am using numbers to initialize the Chars. As numbers are doubles, for the short moment of initialization I assume more memory is needed, but not at rest.
Further, I am not entirely sure what extending a TypedArray does to its instance's size.
old answer
A Char is simply a number. Since the basic types of TypeScript do not include Chars what you could do is store a number and convert it back and forth:
var number = "h".charCodeAt(0);
var char = String.fromCharCode(number)
And to something like:
class Char {
private _value:Number;
constructor(char: Number | String){
this.setValue(char);
}
get getValue():String {
return String.fromCharCode(this._value);
}
set setValue(char: Number | String) {
if (typeof char === "number") {
this._value = char;
}
else {
this._value = char.charCodeAt(0);
}
}
}
As I know, the basic types of typescript don't include char.
As a slightly more concise method than listing out all letter characters, you can list out all upper-case or all lower-case characters, then use Lowercase or Uppercase to change their capitalization and unify with the previous type.
type UpperCaseCharacter = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z';
// add additional non-letter characters to this union as desired
type Character = UpperCaseCharacter | Lowercase<UpperCaseCharacter>;
You could just define a wrapper around string, and throw an error if the string is more than one character.
class Character {
readonly char: string;
constructor(char: string) {
if(char.length !== 1) {
throw new Error(char + " is not a single character");
}
this.char = char;
}
toString(): string {
return this.char;
}
}
////////////////////////////////////////
var good: Character = new Character("f");
var bad: Character = new Character("foo"); //error
Of course, you can also add helper methods to the class which operate on the string like toLowerCase(), toUpperCase(), etc.
What does the +d in
function addMonths(d, n, keepTime) {
if (+d) {
mean?
The + operator returns the numeric representation of the object. So in your particular case, it would appear to be predicating the if on whether or not d is a non-zero number.
Reference here. And, as pointed out in comments, here.
Operator + is a unary operator which converts the value to a number. Below is a table with corresponding results of using this operator for different values.
+----------------------------+-----------+
| Value | + (Value) |
+----------------------------+-----------+
| 1 | 1 |
| '-1' | -1 |
| '3.14' | 3.14 |
| '3' | 3 |
| '0xAA' | 170 |
| true | 1 |
| false | 0 |
| null | 0 |
| 'Infinity' | Infinity |
| 'infinity' | NaN |
| '10a' | NaN |
| undefined | NaN |
| ['Apple'] | NaN |
| function(val){ return val }| NaN |
+----------------------------+-----------+
Operator + returns a value for objects which have implemented method valueOf.
let something = {
valueOf: function () {
return 25;
}
};
console.log(+something);
It is a unary "+" operator which yields a numeric expression. It would be the same as d*1, I believe.
As explained in other answers it converts the variable to a number. Specially useful when d can be either a number or a string that evaluates to a number.
Example (using the addMonths function in the question):
addMonths(34,1,true);
addMonths("34",1,true);
then the +d will evaluate to a number in all cases. Thus avoiding the need to check for the type and take different code paths depending on whether d is a number, a function or a string that can be converted to a number.
I have the following regex patterns that matches all the 'act' that ends with numbers within a list of URLs.
Regex pattern /\/ch(\d+)\/act(\d+)/gi
Javascript code
pageSrc[1] = "../ch01/index.html";
pageSrc[2] = "../ch01/act01/1.html";
pageSrc[3] = "../ch01/act01/2.html";
pageSrc[4] = "../ch01/act02/1.html";
pageSrc[5] = "../ch01/act02/2.html";
pageSrc[6] = "../ch01/actx/1.html";
var pattern = /\/ch(\d+)\/act(\d+)/gi;
for(var i=0; i<pageSrc.length; ++i){
var hasAct = pattern.test(pageSrc[i]);
console.log(hasAct);
}
The expected results and actual results
| String | Expected Result | Actual Result |
| pageSrc[1] | false | false |
| pageSrc[2] | true | true |
| pageSrc[3] | true | *FALSE |
| pageSrc[4] | true | true |
| pageSrc[5] | true | *FALSE |
| pageSrc[6] | false | false |
Am not sure why pageSrc[3] won't return true. I used the regEx tester on gskinner.com and it worked fine, here is the link http://gskinner.com/RegExr/?344ap
Can anyone help me take a look please? thanks in advance!
Remove the g flag. From the RegExp.test documentation:
As with exec (or in combination with it), test called multiple times on the same global regular expression instance will advance past the previous match.
You don't want a global search when reusing a pattern like this.
> var pageSrc = [];
> pageSrc[1] = "../ch01/index.html";
pageSrc[2] = "../ch01/act01/1.html";
pageSrc[3] = "../ch01/act01/2.html";
pageSrc[4] = "../ch01/act02/1.html";
pageSrc[5] = "../ch01/act02/2.html";
pageSrc[6] = "../ch01/actx/1.html";
var pattern = /\/ch(\d+)\/act(\d+)/i;
for(var i=0; i<pageSrc.length; ++i){
var hasAct = pattern.test(pageSrc[i]);
console.log(i, hasAct);
}
0 false
1 false
2 true
3 true
4 true
5 true
6 false
You are using /g. Remove that flag to make it work.
The g flag makes the regex start matching from pattern.lastIndex (the index where the previous match ended), until it fails, and then start at 0.