Dart Regex Matching - dart

I want to check via regex in dart whether a line contains string such as ABS_D0 or ABS_D1, or ABS_D2 etc upto ABS_D40 and also ABS_DX.
var dcc1= "ABS_D0 4, 5, 158, b";
var dcc2 = "ABS_D1 3, 5, 157, b";
var dccEnd = "ABS_DX";
If line contains matching string then line is split via comma and stored in list.
example
ABS_D0 4, 5, 158, b should become list[0]=0,list[1]=4,list[2]=5,list[3]=158,list[4]=b
ABS_D1 3, 5, 157, b should become list[0]=1,list[1]=3,list[2]=5,list[3]=157,list[4]=b

You are not saying which tpe the list elements must have. The one containing "b" is clearly a string, but should 158 be a string or an integer?
I'll make it a string for now, you can always use int.parse if you want it as an integer.
final absRE = RegExp(r"ABS_D([1-4]?\d|X)\s*");
List<String> matchABS(String line) {
var match = absRE.firstMatch(line);
if (match == null) return null;
var result = [match[1]]
result.addAll(line.substring(match.end).split(",").map((s) => s.trim());
return result;
}
The regular expression matches "ABS_D" followed by either a number in the range 0..40 (well, it accepts up to 49 actually, but I assume that's not a problem) or "X". Then the code splits the rest of the line on commas.

Related

Is it possible to remove duplicates in Dart with another variable

I have searched a lot for removing duplicates from a list in Dart using ANOTHER variable.
Here is what I mean:
List<int> numbers = [1, 2, 3, 4];
// This list has 4 new elements than the first one
List<int> moreNumbers = [1, 2, 3, 4, 5, 6, 7, 8];
// Now I want to push the moreNumbers unique elements to the numbers one
I want to push it so the end result for the numbers variable should be:
[1, 2, 3, 4, 5, 6, 7, 8];
Is it possible?
void main() {
var lst = [1,2,3,4];
var lst2 = [1,2,3,4,5,6,7,8];
var s = {...(lst+lst2)};
print(s.toList());
}
The trivial approach would be:
for (var number in moreNumbers) {
if (!numbers.contains(number)) {
numbers.add(number);
}
}
Not particularly efficient if numbers is long, because contains on a list can take time proportional to the length of the list.
The time/space trade-off would be creating a set from numbers, because sets have cheap contains:
var alsoNumbers = numbers.toSet(); // Also linear, but only happens once.
for (var number in moreNumbers) {
if (alsoNumbers.add(number)) { // true if elements was added
numbers.add(number);
}
}
(Using add instead of contains ensures that you update the set with new values, so you won't add the same new value twice.)
If you could just make numbers a Set to begin with, it would be much easier to avoid duplicates, just do numbers.addAll(moreNumbers).

How I build Dart Regexp Properly?

Goal of this expression is separate mathematic calculations into operators, symbols, numbers and brackets.
For example:
Input string: 1+3-6*(12-3+4/5)
Output list: 1, +, 3, -, 6, *, (12-3+4/5)
So I built this expression.
It is working on the web page, but in the Dart code this happens:
final calculationExpression = RegExp(
r"/(\(([a-zA-Z0-9-+/*]+)\))|([a-zA-Z0-9]+)|([+/*-]{1})/g",
unicode: true,
multiLine: true,
);
...
List<String> operators = calculationsString.split(calculationExpression); /// Output: ["", "+", "-", ...]
What did I do wrong?
The syntax /pattern/g is used to create regular expression literals in JavaScript (and sed and some other languages), just as quotes are used to create string literals. Dart doesn't have regular expression literals; you instead must invoke the RegExp constructor directly. Combining a regular expression literal syntax with an explicitly constructed RegExp object makes no sense. When you do RegExp(r'/pattern1|pattern2|pattern3/g'), you're actually matching against /pattern1 (pattern1 prefixed with a literal / character) or pattern2 or pattern3/g (pattern3 followed by a literal string /g).
String.split does not split the input string such that each element of the result matches the pattern. It treats all matches of the pattern as separators. Consequently, the resulting list will not have any elements that match the pattern, which is the opposite of what you want. You instead want to find all matches of the pattern in the string. You instead can use RegExp.allMatches if you additionally verify that the input string contains only matches from the regular expression.
Putting it all together:
void main() {
final calculationExpression = RegExp(
r"(\(([a-zA-Z0-9-+/*]+)\))|([a-zA-Z0-9]+)|([+/*-]{1})",
unicode: true,
multiLine: true,
);
var calculationsString = '1+3-6*(12-3+4/5)';
// Prints: [1, +, 3, -, 6, *, (12-3+4/5)]
print(calculationsString.tokenizeFrom(calculationExpression).toList());
}
extension on String {
Iterable<String> tokenizeFrom(RegExp regExp) sync* {
void failIf(bool condition) {
if (condition) {
throw FormatException(
'$this contains characters that do not match $regExp',
);
}
}
var matches = regExp.allMatches(this);
var lastEnd = 0;
for (var match in matches) {
// Verify that there aren't unmatched characters.
failIf(match.start != lastEnd);
lastEnd = match.end;
yield match.group(0)!;
}
failIf(lastEnd != length);
}
}
You put the JavaScript regexp literal slashes and flags inside the Dart string.
If you remove the leading / and trailing /g, you get the RegExp you intended to.
The multiLine and unicode flags are unnecessary (your regexp doesn't use any feature affected by those)
The Dart split function does not emit capture groups, so you probably want to look at getting the matches, not removing them, which is what split does.
All in all, try:
final calculationExpression = RegExp(
r"\([a-zA-Z\d\-+/*]+\)|[a-zA-Z\d]+|[+/*\-]");
List<String> tokes =
calculationExpression.allMatches(calculationsString).toList();

Convert number input to characters in Google Sheets

How to convert a number entered in google sheets to some characters?
Example:
input in cell: “1234”
output format in cell: “ABCD”
So I’d like to have some logic that will loop through every digit and convert it to a corresponding character of my choice, and have that be what is displayed in the cell.
EDIT: The character <> number representation don't have to be sequential. So we could have 1:"A", 2:"Q", 3:"E" etc. And the conversion should happen in place then replace the input altogether, as if the string was entered initially and not the numbers. Not sure if that can be achieved without a script.
You'll need a script
You can take advantage of an onEdit trigger. This is something that allows a script to run every single time there is an edit on your sheet. Within the trigger, you can tell it to replace the contents of a cell if it meets certain criteria. For instance, if its in a certain range, or it contains a certain value.
Example:
const code = {
'A': 1,
'B': 2,
'C': 5,
'D': 6,
'E': 9,
'F': 8,
'G': 7,
'H': 3,
'I': 2,
'J': 45,
'K': 4,
'L': 32,
'M': 45,
'N': 5,
'O': 4,
'P': 3,
'Q': 6,
}
function onEdit(e) {
if (e.range.columnEnd === 1 &&
e.range.columnStart === 1) {
let letters = e.value.split("")
let numbers = letters.map(letter => code[letter])
let newValue = numbers.join("")
e.range.setValue(newValue)
}
}
Within the code dictionary, you need to define what you want the conversion to be.
Then within the onEdit function:
It checks if the change is in the first column.
If so, it takes the value (e.value) and splits it into an array. So if the value input into the cell was "ABC" then the resulting array would be ["A","B","C"].
Then it map the array using the code. This just goes through the array and returns whatever is in the dictionary. So now you have [1,2,5].
Then it join the resulting array into a continuous string "125"
Finally, it setValue using the e.range to the newValue.
Room for improvement
You need to make sure you have all the possible characters that someone will enter within the code object. Or you will need to handle the case of what to do with a character that is not in the object. For example, you could just return a space:
let numbers = letters.map(letter => {
if (code.hasOwnProperty(letter)) {
return code[letter]
} else {
return " "
}
You may have an issue with converting numbers to letters. Seeing as there are only 10 single digit numbers (0 - 9). For example, if you have A = 1 and B = 11. If the value in a cell was converted to 11 then you wouldn't know if the original value had been AA or B.
References and further reading
onEdit()
Range
setValue(value)
Event objects
try:
=INDEX(JOIN(, CHAR(64+REGEXEXTRACT(A1&"", JOIN("|", REPT("(.)", LEN(A1)))))))
suggested approach:
=INDEX(JOIN(, CHAR(64+SPLIT(A3, " "))))

Is it possible to initialize a List on one line in Dart? (it's called a collection initializer in c#)

Is it possible to initialize a list on one line in Dart? Something like the following...
List<int> options = new List<int>{ 1,2,5,9 };
(this is possible in c# and is called a collection initializer)
Yes:
List<int> options = [1, 2, 5, 9];
I'd recommend reading:
https://api.dartlang.org/stable/1.24.3/dart-core/List-class.html
Yes, you can do it using the List.unmodifiable constructor:
var options = new List.unmodifiable([3,6,7,8]);
Or by using the List.from constructor:
var options = new List.from([3,6,7,8]);
Or just like this:
var options = [5,7,9,0];
There are also available List.filled and List.generate factory constructors:
List<int?> s = List.filled(5, 10, growable: true); // [10, 10, 10, 10, 10]
This creates list of length 5, of type int or null, and initializes each element with 10. This list is growable, which means its length can be changed with a setter:
s.length = 10;
s[8] = 2; // [10, 10, 10, 10, 10, null, null, null, 2, null]
After changing the list length, new elements will be initialized with null. If the list element type is not-nullable this will cause Exception.
List.generate generates a list of values.
var n = List.generate(5, (index) => 0); // [0, 0, 0, 0, 0]
The created list is fixed-length, and each element is set to 0.
List<int?> n = List.generate(5, (index) => index * index, growable: true); // // [0, 1, 4, 9, 16]
If we want to create growable list (i.e. we set growable to true) we need to explicitly choose non-nullable type eg. int? as we did here, otherwise increasing list length will raise exception. This stands for both List.generate and List.filled factories.
Good reads about those are:
https://api.dart.dev/stable/1.24.3/dart-core/List/List.generate.html
and
https://api.dart.dev/stable/1.24.3/dart-core/List/List.filled.html
var vals = <int>[1, 2, 3];
var vals2 = List<int>()..addAll([1, 2, 3]);
var vals3 = List<int>.of([1, 2, 3]);
Note that when we don't provide a type, we in fact create a list of a
dynamic type. Also, the new keyword is optional.
Square brackets define a List
var listOfInt = [1,2,3]
Curly brackets define a Set
var setOfInt = {1,2,3};
Curly brackets with colons define a Map
var mapOfIntString = {1: "a", 2: "b"};
It is possible to specify the type explicitly.
var list = <int>[1,2,3]
var setOfInt = <int>{1,2,3};`
var map = <int,String>{1: "a", 2: "b"};
Initialize empty list
List<int> options = [];
Initialize filled list
List<int> options = [1,2,5,9];

Comma-delimited and dash-delimited text fields in MVC 4

We have a Razor form in our web app. During a meeting with the clients, we learned that one of the fields in this form should accept input that is:
A list of comma-delimited numbers
including ranges of numbers expressed using dashes
For example, the end-users might enter 45,50-53,65 to represent the list of numbers 45, 50, 51, 52, 53, 65. I'm assuming that the textbox might contain arbitrary whitespace as well that should be ignored (so 45, 50-53, 65 would represent the same information).
How would I set up such a text box in MVC 4 using Razor? In particular,
How would I create the text box in my Razor view?
How would I represent the information in the text box in my model?
How would I data-bind the text box to the model?
How would I set up validation for the text box?
I would just create a normal textbox -- you're asking for a string of numbers
You could have the form value as a string, and then another property that is the parsed version of the string, int[].
Since it is a string, it can be posted as a string.
Use a regular expression for the validator.
For 2, you could do something like this in your model:
public string Numbers { get; set; }
public int[] ParsedNumbers
{
get
{
Func<int[], int[]> arrayToRange = (range) =>
{
if (range.Length == 1) return range;
int[] ret = new int[range[1] - range[0] + 1];
for (int i = 0; i < ret.Length; i++)
{
ret[i] = i + range[0];
}
return ret;
};
return this.Numbers
.Replace(" ", "")
.Split(',')
.SelectMany(n => arrayToRange(n.Split('-')
.Select(n2 => int.Parse(n2)).ToArray())).ToArray();
}
}
For 4, you could use this RegEx:
^(\d(\-\d)?(, ?)?)*$

Resources