Does Google Dart have something like regex.exec()? - dart

I read the documentation (https://api.dartlang.org/stable/1.21.1/dart-core/RegExp-class.html) but could not find I was looking for. Either I didnt understand it or I overlooked something.
I am trying to replicate the following in google dart:
var regex = /foo_(\d+)/g,
str = "text foo_123 more text foo_456 foo_789 end text",
match = null;
while (match = regex.exec(str)) {
console.log(match); // matched capture groups
console.log(match.index); // index of where match starts in string
console.log(regex.lastIndex); // index of where match ends in string
}
I also created a jsfiddle: https://jsfiddle.net/h3z88udz/
Does dart have something like regex exec()?

RegExp.allMatches looks like it does what you want.
var regex = new RegExp(r"foo_(\d+)");
var str = "text foo_123 more text foo_456 foo_789 end text";
void main() {
for (var match in regex.allMatches(str)) {
print(match);
print(match.start);
print(match.end);
}
}
https://dartpad.dartlang.org/dd1c136fa49ada4f2ad4ffc0659aab51

Related

Import JSON API into Google Sheets

I need to import some information from a JSON API URL into Google Sheets.
This is one example:
https://api-apollo.pegaxy.io/v1/game-api/race/details/69357391
I've been successful in importing basic information using IMPORTJSON available on Github:
https://github.com/bradjasper/ImportJSON/
But now I am faced with a type of information (is it an object? an array?) which seems to be different from the usual and I find myself unable to import this.
Here is a piece of it:
{
"id": 969228010,
"raceId": 69357391,
"pegaId": 20042,
"gate": 8,
"pegaAttributes": "{\"id\":20042,\"name\":\"Bajaj\",\"ownerId\":623299,\"raceClass\":1,\"races\":1369,\"win\":504,\"lose\":865,\"energy\":18,\"gender\":\"Male\",\"bloodLine\":\"Campona\",\"breedType\":\"Legendary\",\"speed\":4.95,\"strength\":0.33,\"wind\":3.36,\"water\":1.84,\"fire\":8.83,\"lighting\":6.93,\"position\":4000,\"finished\":true,\"raceTime\":35.855,\"result\":8,\"gate\":8,\"lastSpeed\":22.721521955555556,\"stage\":4,\"isTopSpeedReached\":false,\"bonusStage\":false,\"topSpeed\":22.721521955555556,\"s0\":0,\"j0\":-0.02,\"a0\":0.4982185622222222,\"v0\":20.127527583333332,\"t0\":179.60000000000002,\"gears\":{},\"pb\":0}"**,
"position": 11,
"raceTime": 35.855,
"reward": 0
},
So using IMPORTJSON if I wanted to simply import the "raceId" element I'd go about doing this:
=ImportJSON("https://api-apollo.pegaxy.io/v1/game-api/race/details/69357391", "/race/registers/raceId", "noHeaders")
But when trying to import any information from within pegaAttributesthe IMPORTJSON is unable to recognize it as separate. The best I can do is import the whole block like so:
=ImportJSON("https://api-apollo.pegaxy.io/v1/game-api/race/details/69357391", "/race/registers/pegaAttributes", "noHeaders")
So some of the information after "pegaAttributes" and inside brackets { } I need to import. For example the attributes raceTime , topSpeed, lastSpeed and so on, how can I import this into Google Sheets?
Could anyone provide any pointers on how to do this? Thank you.
Try (you will have to apply JSON.parse on the pegaAttributes element which is also a json)
=importDataJSON(url,"id|position|raceTime","name|raceTime|topSpeed|lastSpeed")
with
function importDataJSON(url, items1, items2) {
let result = []
result = [[items1.split('|'), items2.split('|')].flat()]
const obj = JSON.parse(UrlFetchApp.fetch(url).getContentText())
obj.race.registers.forEach(o => {
let prov = []
items1.split('|').forEach(item1 => prov.push(o[item1]))
var pegaAttributes = JSON.parse(o.pegaAttributes)
items2.split('|').forEach(item2 => prov.push(pegaAttributes[item2]))
result.push(prov)
})
return result
}
with as parameters:
url
items1 (level 1) separated by |
items2 (level2, under pegaAttributes) separated by |
new edit
=importDataJSON(url,"totalReward|length","id|position|raceTime","name|raceTime|topSpeed|lastSpeed")
with
function importDataJSON(url, items0, items1, items2) {
let result = []
result = [[items0.split('|'), items1.split('|'), items2.split('|')].flat()]
const obj = JSON.parse(UrlFetchApp.fetch(url).getContentText())
let prov = []
items0.split('|').forEach(item0 => prov.push(obj.race[item0]))
result.push(prov)
obj.race.registers.forEach(o => {
let prov = []
items0.split('|').forEach(item0 => prov.push(''))
items1.split('|').forEach(item1 => prov.push(o[item1]))
var pegaAttributes = JSON.parse(o.pegaAttributes)
items2.split('|').forEach(item2 => prov.push(pegaAttributes[item2]))
result.push(prov)
})
return result
}
You have to parse it twice as that's an object just as text. I think using the custom formula might not be easiest since Google App Scripts can do this for you pretty cleanly. Consider using the standard JSON.parse() functions.
The below function got me the following values you were looking for. See the debug screen shot.
function getJSONData(){
const zURL = 'https://api-apollo.pegaxy.io/v1/game-api/race/details/69357391';
var response = UrlFetchApp.fetch(zURL);
var cleanedResponse = JSON.parse(response);
var theRace = cleanedResponse['race'];
var theRegisters = theRace['registers'];
var aRegister = theRegisters[0];
var oneID = oneRegister.id;
var aGate = oneRegister.gate;
var aPega = oneRegister.pegaAttributes;
var cleanedPega = JSON.parse(aPega);
var zTopSpeed = cleanedPega.topSpeed;
}
If you debug this, function and check to the right in your variables, you should be able to get everything you need. You'll have to find a way to get it back into sheets, but the values are available.
Updated
A request was made to figure out how this could be run as a Sheets Function. leveraging Mike Steelson's approach and presumption for what is needed as far as races... here's a function that could be used. Just paste the URL in the formula.
function getDataMyJSON(theURL) {
const data = JSON.parse(UrlFetchApp.fetch(theURL).getContentText())
const items = ['raceTime','topSpeed','lastSpeed']
let result=[]
data.race.registers.forEach(x => {
let prov = []
prov.push(x.raceId)
var p = JSON.parse(x.pegaAttributes)
items.forEach(i => prov.push(p[i]))
result.push(prov)
})
return result;
}
So then put the URL in the formula and you'd get this...

Guess Look behind caused a blank page on iOS device [duplicate]

I am looking for an alternative for this:
(?<=\.\d\d)\d
(Match third digit after a period.)
I'm aware I can solve it by using other methods, but I have to use a regular expression and more importantly I have to use replace on the string, without adding a callback.
Turn the lookbehind in a consuming pattern and use a capturing group:
And use it as shown below:
var s = "some string.005";
var rx = /\.\d\d(\d)/;
var m = s.match(/\.\d\d(\d)/);
if (m) {
console.log(m[1]);
}
Or, to get all matches:
const s = "some string.005 some string.006";
const rx = /\.\d\d(\d)/g;
let result = [], m;
while (m = rx.exec(s)) {
result.push(m[1]);
}
console.log( result );
An example with matchAll:
const result = Array.from(s.matchAll(rx), x=>x[1]);
EDIT:
To remove the 3 from the str.123 using your current specifications, use the same capturing approach: capture what you need and restore the captured text in the result using the $n backreference(s) in the replacement pattern, and just match what you need to remove.
var s = "str.123";
var rx = /(\.\d\d)\d/;
var res = s.replace(rx, "$1");
console.log(res);

How to define different indentation levels in the same document with Xtext formatter

Is it possible to format a document as follows, using Xtext formatting? As you can see, Test children are indented with 4 spaces while External children are indented with 2 spaces only. I am using Xtext 2.12.0.
Test my_prog {
Device = "my_device";
Param = 0;
}
External {
Path = "my_path";
File = "my_file";
}
you could try to work with custom replacers, dont know if this will work with nested block though
def dispatch void format(External model, extension IFormattableDocument document) {
model.regionFor.keyword("}").prepend[newLine]
for (l : model.ids) {
val region = l.regionFor.feature(MyDslPackage.Literals.IDX__NAME)
region.prepend[newLine]
val r = new AbstractTextReplacer(document, region) {
override createReplacements(ITextReplacerContext it) {
val offset = region.offset
it.addReplacement(region.textRegionAccess.rewriter.createReplacement(offset, 0, " "))
it
}
}
addReplacer(r)
}
}

is there a syntax to call named overload operator functions directly

Trying to invert a map and the following approach works fine:
var x = { 'dart' : 'fun', 'dentist' : 'painful', };
var xInv = x.keys.fold({}, (prev, elm) { prev[x[elm]] = elm; return prev; });
Is there a syntax to call 'operator[]=( , )' directly so you could also use cascades? Something like these which do not work:
var xInv = x.keys.fold({}, (prev, elm) => prev..'operator[]='(x[k], k));
var xInv = x.keys.fold({}, (prev, elm) => prev..operator[]=(x[k], k));
This is not what you want?
Map<String,String> xInv2 = x.keys.fold({},
(Map<String,String> prev, String elm) => prev..[x[elm]] = elm);
Creates the same result as your first example with the return statement.
(You don't have to add to add the type annotations though. It's just easier for me to grasp the intention of the code)

What grammar is this?

I have to parse a document containing groups of variable-value-pairs which is serialized to a string e.g. like this:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
Here are the different elements:
Group IDs:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
Length of string representation of each group:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
One of the groups:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14 ^VAR1^6^VALUE1^^
Variables:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
Length of string representation of the values:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
The values themselves:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
Variables consist only of alphanumeric characters.
No assumption is made about the values, i.e. they may contain any character, including ^.
Is there a name for this kind of grammar? Is there a parsing library that can handle this mess?
So far I am using my own parser, but due to the fact that I need to detect and handle corrupt serializations the code looks rather messy, thus my question for a parser library that could lift the burden.
The simplest way to approach it is to note that there are two nested levels that work the same way. The pattern is extremely simple:
id^length^content^
At the outer level, this produces a set of groups. Within each group, the content follows exactly the same pattern, only here the id is the variable name, and the content is the variable value.
So you only need to write that logic once and you can use it to parse both levels. Just write a function that breaks a string up into a list of id/content pairs. Call it once to get the groups, and then loop through them calling it again for each content to get the variables in that group.
Breaking it down into these steps, first we need a way to get "tokens" from the string. This function returns an object with three methods, to find out if we're at "end of file", and to grab the next delimited or counted substring:
var tokens = function(str) {
var pos = 0;
return {
eof: function() {
return pos == str.length;
},
delimited: function(d) {
var end = str.indexOf(d, pos);
if (end == -1) {
throw new Error('Expected delimiter');
}
var result = str.substr(pos, end - pos);
pos = end + d.length;
return result;
},
counted: function(c) {
var result = str.substr(pos, c);
pos += c;
return result;
}
};
};
Now we can conveniently write the reusable parse function:
var parse = function(str) {
var parts = {};
var t = tokens(str);
while (!t.eof()) {
var id = t.delimited('^');
var len = t.delimited('^');
var content = t.counted(parseInt(len, 10));
var end = t.counted(1);
if (end !== '^') {
throw new Error('Expected ^ after counted string, instead found: ' + end);
}
parts[id] = content;
}
return parts;
};
It builds an object where the keys are the IDs (or variable names). I'm asuming as they have names that the order isn't significant.
Then we can use that at both levels to create the function to do the whole job:
var parseGroups = function(str) {
var groups = parse(str);
Object.keys(groups).forEach(function(id) {
groups[id] = parse(groups[id]);
});
return groups;
}
For your example, it produces this object:
{
'1': {
VAR1: 'VALUE1'
},
'4': {
VAR1: 'VALUE1',
VAR2: 'VAL2'
}
}
I don't think it's a trivial task to create a grammar for this. But on the other hand, a simple straight forward approach is not that hard. You know the corresponding string length for every critical string. So you just chop your string according to those lengths apart..
where do you see problems?

Resources