How to split a string from a pattern - javascript

I want to be able to split a string from a repeating pattern. For exemple:
id||eq||2,id||eq||1
will give me id||eq||2 and id||eq||1
this is easy because the separator is ,. But unfortunately I can have the separator inside the splitted part:
id||eq||2,2,id||eq||1
and I would like to get id||eq||2,2 and id||eq||1
I tried something like this: (\w+\|{2}\w+\|{2}[\w,]+,?)
But it always take the first part of the next group and not the second group
id||eq||1,2,id||eq||2,1
I'm out of ideas, if some of you can help me ?
EDIT
To be more precise, I want to get an array of objects (lets call it RequestFilter[]) from an url (and the param in the url is already an array).
An object RequestFilter looks like this:
fieldwhich is a string and can only contain alphanumeric chars
type which is an enum, can either contain only alphanumeric chars
filter which can be any char ( like ,)
which give me this in the url:
?filter[]=field||type||filter
today I already get RequestFilter from an url, but now I have to get an array of RequestFilter. I could use any separator, but because the attribute filter can be anything there will always be a risk of conflict with it while splitting.
some more examples of strings I can have and the expected RequestFilter[]:
name||cont||pier
[{field: 'name', type: 'cont', filter: 'pier'}]
name||cont||a,id||in||2,3
[{field: 'name', type: 'cont', filter: 'pier'},{field: 'id', type: 'in', filter: '2,3'}]
id||in||2,3,4,5,address||eq||Paris,France
[{field: 'id', type: 'in', filter: '2,3,4,5'},{field: 'address', type: 'eq', filter: 'Paris,France'}]
EDIT 2
I was pretty sure it was possible to handle it with Regex but if you think it's not possible just tell me and I will try to find another way to handle it.

A solution using Negative lookahead assertion:
const str = 'id||eq||2,id||eq||1';
const str2 = 'id||eq||2,2,id||eq||1';
console.log(
str.split(/(?!,\d),/)
)
console.log(
str2.split(/(?!,\d),/)
)

str = 'id||eq||2,2,id||eq||1'
splitted = str.split('id||eq||')
splitted.forEach(function(item, index, arr) {
arr[index]= 'id||eq||'+item;
})
splitted.shift()
console.log(splitted)

You can split it by ',id' and then do some manipulations like below
let str='id||eq||2,2,id||eq||1'
//split by ',id'
let arr=str.split(',id');
//join with fixed pattern
let result=arr.join('___id');
//split with fix pattern again
let final_arr=result.split('___');
console.log(final_arr);

Related

Regex matching of range with pre-defined labels

I have the following regex that allows any number (x), or comma separated numbers (x,x), or any range (x-x) for an input on my page:
^\d+(?:(?:\s*-\s*\d+)?|(?:\s*,\s*\d+)*)$
This works perfectly but I have a new requirement. I need to allow the user to label what they are defining and allow that if it's valid. For example, they can say: Buildings: 1000-1010 or Buildings: 1001,1002.
Buildings: must be in the beginning of the string followed a colon followed by the rest. If they don't specify I won't know what they are saying.
Currently Buildings is the only label they can define, but I know more will be coming so I would also like to be able to specify that, if possible. But hopefully I can figure it out if I figure out how to allow one:
let input = 'Buildings: 1000-1001';
/^\d+(?:(?:\s*-\s*\d+)?|(?:\s*,\s*\d+)*\s*,?)$/.test(input); // should be ok
input = 'Buildings: 1000';
/^\d+(?:(?:\s*-\s*\d+)?|(?:\s*,\s*\d+)*\s*,?)$/.test(input); // should be ok
input = 'Buildings: 1000,2002';
/^\d+(?:(?:\s*-\s*\d+)?|(?:\s*,\s*\d+)*\s*,?)$/.test(input); // should be ok
For future, say I have an array of labels:
const categories: ['Buildings', 'Cars', 'Houses'];
To loop over this list and run the test and include the variable's value in the regex. Is it possible?
Are you supposed to hardcode the text? I started trying that but I kept getting errors.
Thank you
A simpler regex should do the trick along with some format strings.
for example a simpler regex like /\d+?([,-]\d+?)*?$/ should do fine matching all three of your examples.
And for the string substitution using the new RegExp syntax will allow you to use standard format strings.
For Example:
const categories = ['Buildings', 'Cars', 'Houses'];
categories.forEach((i) => {
let input = 'Buildings: 1000-1001';
let re = new RegExp(`${i}: \\d+?([,-]\\d+?)*?$`);
console.log(re.test(input));
input = 'Buildings: 1000';
re = new RegExp(`${i}: \\d+?([,-]\\d+?)*?$`);
console.log(re.test(input));
input = 'Buildings: 1000,2002,2003,2034';
re = new RegExp(`${i}: \\d+?([,-]\\d+?)*?$`);
console.log(re.test(input));
input = 'Buildings: 1000,2002';
re = new RegExp(`${i}: \\d+?([,-]\\d+?)*?$`);
console.log(re.test(input));
});
OUTPUT : Only the first 4 print True because Only the first element of the array is in each of the inputs.
true
true
true
true
false
false
...
If the labels do not contain any special regex meta character, you might shorten the pattern and use an alternation
^(?:Buildings|Cars|Houses):\s*\d+(?:\s*[-,]\s*\d+)*$
See a regex demo.
const categories = ['Buildings', 'Cars', 'Houses'];
const regex = new RegExp(`^(?:${categories.join("|")}):\\s*\\d+(?:\\s*[-,]\\s*\\d+)*$`);
[
"Buildings: 1000-1001",
"Buildings: 1000",
"Buildings: 1000,2002",
"Test: 1000,2002"
].forEach(s => console.log(`${s} --> ${regex.test(s)}`));

regex match starts and ends with expression using angular

I have my working example here
https://stackblitz.com/edit/angular-fk2vpr?embed=1&file=src/app/app.component.ts
here I wrote condition like if word starts and ends with { and } they need to highlight and in editable way. here its working fine for word which is having no space like {username} but in case of { host} then its not taking that word to highlight and also if somewords like {pas}!!
then word of {pas} alone needs to highlight but its taking along with that !!
Can anyone assist me what mistake I made on this scenario
Thanks in advance
Try with below code it will work.
const editable = data.match(/{+.*?}+/gi);
if(editable){
editable.map(word => {
const re= new RegExp(word,'g');
data = data.replace(re,' '+ word.replace(/\s/gi,'')+' ')
})
}
As per above code the token {username}!! will not work as we are splitting with space. To resolve that problem we can do add extra space before and after of that token. Use below line to work in all condition.
data = data.replace(word, ' ' + word.replace(/\s/g,'') + ' ')
The problem lies with mapping the data.split(' '). Notice that when you call that on a string username { username} scrypt secret {password} you will not get an array with 5 elements, as you would like (i.e ['username', '{ username}', 'scrypt', 'secret', '{password}']), but it will split on every space character; so the resulting array looks more like this:
[
'username', '',
'{', '',
'', 'username}',
'scrypt', 'secret',
'{password}'
]
If you can, I would advice on making the arr array as an array of arrays, each subarray consisting of words you want to launch a regex through. If this is, however, not an option, I would suggest not splitting like that, but mapping like this:
this.mappedArr = this.arr.map(data => {
const editable = data.match('([{].*?[}])');
console.log(editable);
return {
editable,
data: !editable ?
data :
data.match(/(\{+ *)?\w+( *\}+)?/g).map(word => ({word, editable: word.match('^[{].*?[}]$')})) <
};
})
This begs for refactoring though, but it should work as an emergency fix.

Convert string with index in brackets to JSON array

I have various strings with numbers in brackets like "[4]Motherboard, [25]RAM" how can I convert such a string to a JSON array (keeping both ids and values) like this:
{"data":[
{"id":"4","item":"Motherboard"},
{"id":"25","item":"RAM"}
]};
I'm tried using split(",") to create the array but I really can't find out how to get the inner data in this case.
You could use a regular expression, which takes the number and the string, and assign it as property to an object.
var string = "[4]Motherboard, [25]RAM",
data = string.split(', ').map(function (a) {
var p = a.match(/^\[(\d+)\](.+)$/);
return { id: p[1], item: p[2] };
});
console.log(data);
Here one way to do it. The pattern \[(\d+?)\](.+) works like this:
(…) is a capture group. Just means whatever matches within the brackets will be a token in the result.
\d means a digit
\d+ means a digit, one or more times
\d+? means a digit, one or more times, but as few as possibile before the pattern matches something else.
.+ means any character, one or more times.
[ and ] have a special meaning in regular expression, so if you actually want to match the characters themselves, you need to escape them like so \[ and \].
The double backslashes \\ are just a JS oddity when defining a regex via a string as opposed to using a /literal/. Just two ways of saying the same thing.
There's plenty of resources to learn regex syntax, and http://regex101.com is a great place to play with patterns and experiment.
var input = "[4]Motherboard, [25]RAM";
var pattern = '\\[(\\d+?)\\](.+)';
var result = input.split(',').map(function (item) {
var matches = item.match(new RegExp(pattern));
return {id: matches[1], val: matches[2]};
});
console.log(result)
function toArray(string) {
return {
data: string.split(",").map(function(str) {
str = str.trim();
return {
id: str.substring(1, str.indexOf("]")),
item: str.substring(str.indexOf("]") + 1),
};
}),
};
}

Replace multiple strings between characters in JavaScript

I'm trying to replace strings with variables in the same way that Python string formatting works.
The string will be in a similar format to:
string = 'Replace $name$ as well as $age$ and $country$';
And I'd like to have a regex operation that will return and array:
['name', 'age', 'country'] or ['$name$', '$age$', '$country$']
So that I can map it to object keys:
{
name : 'Bob',
age : 50,
country : 'US'
}
I've seen solutions using string concatenation but I need a solution using regex because
I have to rely on strings containing these variables.
You can do this:
var string = 'Replace $name$ as well as $age$ and $country$';
var arr = [];
string.replace(/\$(.+?)\$/g, function(m,g1){ arr.push(g1); return m; });
console.log(arr); // desired output

regex capture bind parameters following a colon

I'm trying to match substrings that follow colons : without matching the colon as well. It should be really simple. Given
select * from table where name=:name, id = :id order by :order_by limit :limit
it should match
name
id
order_by
limit
However, it's matching
:name
:id
:order_by
:limit
The regex I'm using is
:([a-zA-Z0-9_]+)
but I've also tried
(?::)([a-zA-Z0-9_]+)
according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#special-non-capturing-parentheses.
Can someone help me?
Your regexes should work. Assuming you are using JavaScript, you can collect you findings like this in the matches array:
var myRe = /:(\w+)/g;
var str = "select * from table where name=:name, id = :id order by :order_by limit :limit";
var matches = [];
var myArray;
while ((myArray = myRe.exec(str)) !== null) {
matches.push(myArray[1]);
}
See: http://jsfiddle.net/6CB5Y/1/
myArray is an array containing the whole match (e.g. ':name') and all its parenthesized substring matches, if any (e.g. 'name'). So use myArray[1] to just collect the parenthesized match.
The non-capturing parentheses are still used to the form the $0 or full match, i.e.
:name
+---+ 0
+--+ 1
You probably want to perform a replacement on those place holders, so I would solve the lack of look-behind by using a replacement function:
var bound = {
name: 'test',
id: 'world',
order_by: 'col',
limit: 123
},
sql = 'select * from table where name=:name, id = :id order by :order_by limit :limit';
sql.replace(/:(\w+)/g, function($0, $1) {
// TODO apply escaping
return bound[$1]; // perform lookup using 'name', 'id', etc.
});
// "select * from table where name=test, id = world order by col limit 123"
You just need a "look behind":
(?<=:)\w+
A look behind asserts, but does not capture, what comes before the match.
Note the the whole match (not group 1 as in your regex) is your target.
Also notice the simplification: \w means exactly [a-zA-Z0-9_] .
See this regex running on rubular

Categories