Invert capturing groups in regular expression - javascript

Given this (note the capturing groups):
/^\w+\[\w+\]\[\w+\]\[(\d+)\]\[\w+\]\[(\d+)\]\[\w+\]$/
I want this (note the capturing groups have been inverted):
/^(\w+\[\w+\]\[\w+\]\[)\d+(\]\[\w+\]\[)\d+(\]\[\w+\])$/
What I have attempted:
str = /^\w+\[\w+\]\[\w+\]\[(\d+)\]\[\w+\]\[(\d+)\]\[\w+\]$/.toString()
noncaptured = [];
captured = [];
insideCapture = false;
for (var i = 0, position = 1; i < str.length; i++) {
if( str.charAt(i) != '(' && insideCapture == false ){
noncaptured.push([position, str.charAt(i) ] );
} else {
if( str.charAt(i) != '(' ){
position += 1;
}
captured.push([position, str.charAt(i) ]);
insideCapture = true;
if( str.charAt(i) == ')' ){
captured.push([position, str.charAt(i)]);
insideCapture = false;
position += 1;
}
}
}
var arr = captured.concat(insideCapture);
arr.sort(function(){
if (a[0] === b[0]) {
return 0;
}
else {
return (a[0] < b[0]) ? -1 : 1;
}
})
I am looking for a clean algorithm. ES6 solution welcome.

Even if I'm not 100% sure the next will work on all the cases you may need, this can be an alternative approach: Reverse all parentheses, and add parentheses after ^ and before $
const str1 = /^\w+\[\w+\]\[\w+\]\[(\d+)\]\[\w+\]\[(\d+)\]\[\w+\]$/.toString();
const str2 = /^\w+(\[\w+\])\[\w+\]\[(\d+)\]\[\w+\]\[(\d+)\]\[\w+\]$/.toString();
const str3 = /^(\w+\[\w+\]\[\w+\]\[\d+\]\[\w+\]\[\d+\]\[\w+\])$/.toString();
const str4 = /^\w+\[\w+\]\[\w+\]\[\d+\]\[\w+\]\[\d+\]\[\w+\]$/.toString();
const replaceMap = {"(": ")", ")": "(", "^": "^(", "$": ")$"};
const reverse = (str) =>
{
return str.replace(/[(,),^, $]/g, function(match)
{
return replaceMap[match];
});
}
console.log(reverse(str1));
console.log(reverse(str2));
console.log(reverse(str3));
console.log(reverse(str4));

Assuming that there won't be nested parentheses, as in the example, I'd slice out the parts of the string between the leading /^ and the trailing $/, then use a regular expression to capture non-parentheses characters, and replace with that group surrounded by parentheses. Also, after the non-parentheses characters, optionally match (s, followed by non-parentheses characters, followed by ), and if that group matches, replace with just that group (without parentheses):
([^(]+)(?:\(([^)]+)\))?
Replace with
($1)$2
https://regex101.com/r/gLrHsH/1
const str = /^\w+\[\w+\]\[\w+\]\[(\d+)\]\[\w+\]\[(\d+)\]\[\w+\]$/.toString();
const sliced = str.slice(2, str.length - 2);
const output = '/^' + sliced.replace(/([^(]+)(?:\(([^)]+)\))?/g, '($1)$2') + '$/';
console.log(output);
([^(]+)(?:\(([^)]+)\))?
means:
([^(]+) - Non-( characters
(?:\(([^)]+)\))? Optional non-capturing group containing:
\( - Leading (
([^)]+) - Non-) characters, captured in a group
\) - Trailing )

Related

how to check first letter of every word in the string and returns next letter in the alphabet [duplicate]

I am build an autocomplete that searches off of a CouchDB View.
I need to be able to take the final character of the input string, and replace the last character with the next letter of the english alphabet. (No need for i18n here)
For Example:
Input String = "b"
startkey = "b"
endkey = "c"
OR
Input String = "foo"
startkey = "foo"
endkey = "fop"
(in case you're wondering, I'm making sure to include the option inclusive_end=false so that this extra character doesn't taint my resultset)
The Question
Is there a function natively in Javascript that can just get the next letter of the alphabet?
Or will I just need to suck it up and do my own fancy function with a base string like "abc...xyz" and indexOf()?
my_string.substring(0, my_string.length - 1)
+ String.fromCharCode(my_string.charCodeAt(my_string.length - 1) + 1)
// This will return A for Z and a for z.
function nextLetter(s){
return s.replace(/([a-zA-Z])[^a-zA-Z]*$/, function(a){
var c= a.charCodeAt(0);
switch(c){
case 90: return 'A';
case 122: return 'a';
default: return String.fromCharCode(++c);
}
});
}
A more comprehensive solution, which gets the next letter according to how MS Excel numbers it's columns... A B C ... Y Z AA AB ... AZ BA ... ZZ AAA
This works with small letters, but you can easily extend it for caps too.
getNextKey = function(key) {
if (key === 'Z' || key === 'z') {
return String.fromCharCode(key.charCodeAt() - 25) + String.fromCharCode(key.charCodeAt() - 25); // AA or aa
} else {
var lastChar = key.slice(-1);
var sub = key.slice(0, -1);
if (lastChar === 'Z' || lastChar === 'z') {
// If a string of length > 1 ends in Z/z,
// increment the string (excluding the last Z/z) recursively,
// and append A/a (depending on casing) to it
return getNextKey(sub) + String.fromCharCode(lastChar.charCodeAt() - 25);
} else {
// (take till last char) append with (increment last char)
return sub + String.fromCharCode(lastChar.charCodeAt() + 1);
}
}
return key;
};
Here is a function that does the same thing (except for upper case only, but that's easy to change) but uses slice only once and is iterative rather than recursive. In a quick benchmark, it's about 4 times faster (which is only relevant if you make really heavy use of it!).
function nextString(str) {
if (! str)
return 'A' // return 'A' if str is empty or null
let tail = ''
let i = str.length -1
let char = str[i]
// find the index of the first character from the right that is not a 'Z'
while (char === 'Z' && i > 0) {
i--
char = str[i]
tail = 'A' + tail // tail contains a string of 'A'
}
if (char === 'Z') // the string was made only of 'Z'
return 'AA' + tail
// increment the character that was not a 'Z'
return str.slice(0, i) + String.fromCharCode(char.charCodeAt(0) + 1) + tail
}
Just to explain the main part of the code that Bipul Yadav wrote (can't comment yet due to lack of reps). Without considering the loop, and just taking the char "a" as an example:
"a".charCodeAt(0) = 97...hence "a".charCodeAt(0) + 1 = 98 and String.fromCharCode(98) = "b"...so the following function for any letter will return the next letter in the alphabet:
function nextLetterInAlphabet(letter) {
if (letter == "z") {
return "a";
} else if (letter == "Z") {
return "A";
} else {
return String.fromCharCode(letter.charCodeAt(0) + 1);
}
}
var input = "Hello";
var result = ""
for(var i=0;i<input.length;i++)
{
var curr = String.fromCharCode(input.charCodeAt(i)+1);
result = result +curr;
}
console.log(result);
I understand the original question was about moving the last letter of the string forward to the next letter. But I came to this question more interested personally in changing all the letters in the string, then being able to undo that. So I took the code written by Bipul Yadav and I added some more code. The below code takes a series of letters, increments each of them to the next letter maintaining case (and enables Zz to become Aa), then rolls them back to the previous letter (and allows Aa to go back to Zz).
var inputValue = "AaZzHello";
console.log( "starting value=[" + inputValue + "]" );
var resultFromIncrementing = ""
for( var i = 0; i < inputValue.length; i++ ) {
var curr = String.fromCharCode( inputValue.charCodeAt(i) + 1 );
if( curr == "[" ) curr = "A";
if( curr == "{" ) curr = "a";
resultFromIncrementing = resultFromIncrementing + curr;
}
console.log( "resultFromIncrementing=[" + resultFromIncrementing + "]" );
inputValue = resultFromIncrementing;
var resultFromDecrementing = "";
for( var i2 = 0; i2 < inputValue.length; i2++ ) {
var curr2 = String.fromCharCode( inputValue.charCodeAt(i2) - 1 );
if( curr2 == "#" ) curr2 = "Z";
if( curr2 == "`" ) curr2 = "z";
resultFromDecrementing = resultFromDecrementing + curr2;
}
console.log( "resultFromDecrementing=[" + resultFromDecrementing + "]" );
The output of this is:
starting value=[AaZzHello]
resultFromIncrementing=[BbAaIfmmp]
resultFromDecrementing=[AaZzHello]

Improving combinations from abc[d[e,f],gh] pattern algorithm

I wrote an algorithm that is inadequate, namely because it does not handle [,abc] cases (see the string variations and conditions below), and would like to know how it can be improved so it covers those cases:
Given
Pattern, that describes strings variations: abc[de[f,g],hk], which gives
abcdef
abcdeg
abchk
Pattern consists of "arrays", that followed by strings: abc[...], and strings adj,kg,q
Another possible more complex example: utvk[fvu,gn[u,k,r],nl,q[t[ij,lo[z,x]],bm]].
Conditions
Strings itself can contain only letters and numbers. There couldn't be abc[h\,k,b] or abc[h\[k,b] that gives abch,k or abch[k.
"Arrays" always not empty, and has at least 2 elements.
There can be any order of "array", or "only string" value, i.e.: abc[a,b[c,d]] or abc[a[b,c],d]. The order is strict from left to right, there can not be from pattern abc[d,e] combinations eabc or dabc.
abc[d,e] doesn't gives abcde nor abced string, only abcd and abce.
Pattern always starts with string with array: something[...].
There can be string without array: abc[a,bc[d,f]], but array without string is not allowed: abc[a,[d,f]].
There can be an empty string, i.e.: a[,b], that gives a and ab
My solution
function getStrings(pat) {
if(pat.indexOf('[') == -1)
return pat;
String.prototype.insert = function(index, string) {
if (index > 0) {
return this.substring(0, index) + string + this.substr(index);
}
return string + this;
};
function getArray(str, start, isSource = false) {
if (start < 0) return null;
var n = 0;
var ret = "";
var i = start;
for (; i < str.length; i++) {
if (str[i] == "[") n++;
else if (str[i] == "]") n--;
if (n == 0) break;
}
var ret = {
str: "",
arr: "",
end: 0,
};
ret.arr = str.slice(start, i) + "]";
ret.end = i;
start--;
var end = start;
for (
;
start > 0 &&
str[start] != "," &&
str[start] != "]" &&
str[start] != "[";
start--
) {}
if(!isSource)
start++;
end++;
ret.str = str.slice(start, end);
return ret;
}
function getElement(source, start) {
var ret = [];
start++;
for (
;
start < source.length && source[start] != "," && source[start] != "]";
start++
)
ret[ret.length] = source[start];
return ret;
}
var source = getArray(pat, pat.indexOf("["), true); // parsing
var ar = source.arr;
source.arrs = getArrays(source); // parsing
source.source = true;
var fi = "";
var temp_out = [];
var tol = 0;
return getVariations(source); // getting variations of parsed
function getVariations(source) {
if (source.arrs == undefined) {
} else
for (var i = 0; i < source.arrs.length; i++) {
if (source.source) fi = source.str;
if (!source.arrs[i].arrs) {
temp_out[tol] = fi + source.arrs[i].str;
tol++;
} else {
var lafi = fi;
fi += source.arrs[i].str;
getVariations(source.arrs[i]);
if(i != source.arrs.length - 1)
fi = lafi;
}
if (source.source && i == source.arrs.length - 1) {
var temp = temp_out;
temp_out = [];
tol = 0;
return temp;
}
}
}
function getArrays(source) {
var la = 1;
var start = 0;
var arrs = [];
if (!source.arr) return;
while (start != -1) {
start = source.arr.indexOf("[", la);
var qstart = source.arr.indexOf(",", la);
if(source.arr[la] == ',')
qstart = source.arr.indexOf(",", la+1);
var pu = false;
if(qstart != la && qstart != -1 && qstart < start && start != -1)
{
pu = true;
var str = source.arr;
var buf = [];
qstart--;
var i = -1;
for(i = qstart; i > 0 && str[i] != '[' && str[i] != ','; i--)
{}
i++;
for(; i < str.length && str[i]!= ','; i++)
{
buf[buf.length] = str[i];
}
if(buf.length == 0)
{
la = start;
alert("1!")
}
else
{
buf = buf.join('');
arrs[arrs.length] = {str:buf};
la += buf.length+1;
}
}
else
if (start != -1) {
arrs[arrs.length] = getArray(source.arr, start);
la = arrs[arrs.length - 1].end + 1;
} else {
start = source.arr.indexOf(",", la);
if (start != -1) {
var ret = getElement(source.arr, start);
arrs[arrs.length] = ret;
la += ret.length;
}
}
}
for (var i = 0; i < arrs.length; i++)
if (typeof arrs[i] != "string" && arrs[i].arr) {
arrs[i].arrs = getArrays(arrs[i]);
var st = arrs[i].arr;
if (occ(arrs[i].arr, "[") == 1 && occ(arrs[i].arr, "]") == 1) {
st = st.replaceAll("[", '["');
st = st.replaceAll("]", '"]');
st = st.replaceAll(",", '","');
st = JSON.parse(st);
for (var j = 0; j < st.length; j++) st[j] = { str: st[j] };
arrs[i].arrs = st;
}
} else if (typeof arrs[i] == "string") {
arrs[i] = { str: arrs[i] };
}
RecursArrs(arrs);
return arrs;
}
function RecursArrs(arrs) {
for (var i = 0; i < arrs.length; i++) {
if (!arrs[i].source)
if (arrs[i].arr) {
delete arrs[i].arr;
delete arrs[i].end;
}
if (!arrs[i].str) {
try{
arrs[i] = { str: arrs[i].join("") };
}catch(er)
{
arrs[i] = {str:''};
}
if (i && arrs[i - 1].str == arrs[i].str) {
arrs.splice(i, 1);
i--;
}
} else if (arrs[i].arrs) RecursArrs(arrs[i].arrs);
}
}
function occ(string, word) {
return string.split(word).length - 1;
}
}
// getStrings('IE5E[COR[R[,G[A,E,I]],S,T,U,V,W,X,Y,Z],EORRG[I,M]]')
I would use a regular expression to break up the input into tokens. In this case I chose to take pairs of (letters, delimiter), where the delimiter is one of "[", "]", ",". The letters part could be empty.
Then I would use a recursive function like you did, but I went for a recursive generator function.
Here is the suggested implementation:
function* getStrings(pattern) {
const tokens = pattern.matchAll(/([^[\],]*)([[\],])?/g);
function* dfs(recur=false) {
let expectToken = true;
while (true) {
const [, token, delim] = tokens.next().value;
if (delim === "[") {
for (const deep of dfs(true)) yield token + deep;
} else {
if (token || expectToken) yield token;
if (delim === "]" && !recur) throw "Invalid pattern: too many `]`";
if (!delim && recur) throw "Invalid pattern: missing `]`";
if (delim !== ",") return;
}
expectToken = delim !== "["; // After [...] we don't expect a letter
}
}
yield* dfs();
}
const input = 'IE5E[COR[R[,G[A,E,I]],S,T,U,V,W,X,Y,Z],EORRG[I,M]]';
for (const s of getStrings(input))
console.log(s);
This implementation should match the patterns according to the given restrictions, but it will also allow the following:
An "array" can start without a prefix of letters. So [a,b] is allowed and will produce the same output as a,b.
An "array" may be followed immediately by letters or a new "array", but this will be interpreted as if they were separated by a comma. So x[a,b]c will be interpreted as x[a,b],c
An "array" can be empty. In that case the array is ignored. So x[] is the same as x.
There is some basic error checking: an error will be generated when the brackets are not balanced.
We can do this in an inside-out fashion. If we replace the innermost group (e.g. 'de[fg]' with its expansion, 'def,deg', and recur until there are no more groups remaining, we will have created a comma-separated list of final strings, which we can simply split apart and return.
const _expand = (
s,
match = s .match (/(.*?)(\w*)\[([^\[\]]+)\](.*)/),
[_, a, b, c, d] = match || []
) => match ? _expand (a + c .split (',') .map (x => b + x) .join (',') + d) : s
const expand = (s) => _expand (s) .split (',')
console .log (expand ('abc[de[f,g],hk]'))
console .log (expand ('utvk[fvu,gn[u,k,r],nl,q[t[ij,lo[z,x]],bm]]'))
.as-console-wrapper {max-height: 100% !important; top: 0}
Our main recursive function -- _expand -- uses a regular expression that extracts the first group, and breaks it into constituent parts, and puts it back together by mapping over the parts of the array. Then our public function, expand simply calls the recursive one and splits the result into an array.
For example, this is how the recursive calls would be handled for the string, 'utvk[fvu,gn[u,k,r],nl,q[t[ij,lo[z,x]],bm]]':
'utvk[fvu,gn[u,k,r],nl,q[t[ij,lo[z,x]],bm]]' //-->
// ^^^^^^^^^
'utvk[fvu,gnu,gnk,gnr,nl,q[t[ij,lo[z,x]],bm]]' //-->
// ^^^^^^^
'utvk[fvu,gnu,gnk,gnr,nl,q[t[ij,loz,lox],bm]]' //-->
// ^^^^^^^^^^^^^^^^^
'utvk[fvu,gnu,gnk,gnr,nl,q[tij,tloz,tlox,bm]]' //-->
// ^^^^^^^^^^^^^^^^^^^
'utvk[fvu,gnu,gnk,gnr,nl,qtij,qtloz,qtlox,qbm]' //-->
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'utvkfvu,utvkgnu,utvkgnk,utvkgnr,utvknl,utvkqtij,utvkqtloz,utvkqtlox,utvkqbm'
Update: Regex explanation:
The regex used here can be broken down into six sections:
(.*?): captures (non-greedy) an initial set of characters, stored as a
(\w*): captures our letters before an opening brace, stored as b
\[: captures an opening brace ([)
([^\[\]]+): captures everything but braces ([ or ]), stored as c
\]: captures a closing brace (])
(.*): captures everything after the closing brace, stored as d
The point is for the group inside the braces to include no other braces. An example might look like this:
utvk[fvu,gn[u,k,r],nl,q[t[ij,lo[z,x]],bm]]
`----+---'\/|`-+-'|`----------+-----------'
\ | \ \ \__ \
| \ \_ \__ \____ \
a: (.*?) \_ \_ \ \ \
~~~~~ | \ \__ \ \
b: (\w*) | \ \ \
~~~~~ | \ \ \
[: \[ | \ \
~~ | \ \
c: ([^\[\]]+) \ \
~~~~~~~~~~ | |
]: \] |
~~ |
d: (.*)
~~~~
Vanialla solution without recursion:
const expander = /([^,[\]]*?)\[([^[\]]*?)]/;
const parse = (fields) => {
let result = fields;
while (result.match(expander)) {
result = result.replace(expander, (m, p1, p2) => p2.split(',').map((e) => `${p1}${e}`).join(','));
}
return result.split(',');
};
console.log(parse('abc[de[f,g],hk]'));
// => [ 'abcdef', 'abcdeg', 'abchk' ]
console.log(parse('utvk[fvu,gn[u,k,r],nl,q[t[ij,lo[z,x]],bm]]'));
// => [ 'utvkfvu', 'utvkgnu', 'utvkgnk', 'utvkgnr', 'utvknl', 'utvkqtij', 'utvkqtloz', 'utvkqtlox', 'utvkqbm' ]
.as-console-wrapper {max-height: 100% !important; top: 0}
Basically I just took the code from object-fields, which one could use as follows
// const objectFields = require('object-fields');
const parse = (input) => objectFields.split(input.replace(/\[/g, '(').replace(/]/g, ')')).map((e) => e.replace(/\./g, ''));
console.log(parse('abc[de[f,g],hk]'));
// => [ 'abcdef', 'abcdeg', 'abchk' ]
console.log(parse('utvk[fvu,gn[u,k,r],nl,q[t[ij,lo[z,x]],bm]]'));
// => [ 'utvkfvu', 'utvkgnu', 'utvkgnk', 'utvkgnr', 'utvknl', 'utvkqtij', 'utvkqtloz', 'utvkqtlox', 'utvkqbm' ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-fields#3.0.1"></script>
Disclaimer: I'm the author of object-fields
Let's describe an algorithm in words. Let's define word as a group of consecutive letters without a comma or bracket, which can also be an empty string. Then one way to think about this process is as a stack with two types of entries:
A word.
An opening bracket, [.
As we traverse the string,
(1) push words and opening brackets onto the stack, not commas.
(2a) when we reach a closing bracket, ], we start a list and keep popping the stack, adding words to that list until we pop an opening bracket from the stack. We then (2b) pop the next entry in the stack, which is the prefix for our current list, and (2c) push each entry from the list onto the stack with the prefix prepended.
Finally, return the stack.
Here's an implementation of the algorithm described above.
function f(s) {
if (s.length == 0) {
return [];
}
const stack = [""];
let i = 0;
while (i < s.length) {
if (s[i] == "[") {
i += 1;
stack.push("[", "");
} else if (s[i] == "]") {
i += 1;
const suffixes = [];
while (true) {
const word = stack.pop();
if (word == "[") {
const prefix = stack.pop();
for (let j = suffixes.length - 1; j >= 0; j--) {
stack.push(prefix + suffixes[j]);
}
break;
} else {
suffixes.push(word);
}
}
} else if (s[i] == ",") {
i += 1;
stack.push("");
} else {
stack[stack.length - 1] += s[i];
i += 1;
}
}
return stack;
}
// Output
var s = "a[bp,c[,d]],b[yx,]"
console.log(s);
for (const w of f(s)) {
console.log(w);
}
console.log("");
s = "abc[de[f,g],hk]"
console.log(s);
for (const w of f(s)) {
console.log(w);
}
Here is a recursion free solution using object-scan.
This solution is probably more of academic interest since it uses library internals and I wrote it to satisfy my curiosity whether it could be done this way. Also serves as a head scratcher for #ScottSauyet - payback for his answer which took me a while to figure out =)
Anyways, enjoy!
.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/object-scan#18.4.0/lib/index.min.js';
import { compile } from 'https://cdn.jsdelivr.net/npm/object-scan#18.4.0/lib/core/compiler.js';
const parse = (input) => {
const compiled = compile([input.replace(/\[/g, '.{').replace(/]/g, '}')], {});
return objectScan(['++{children[*]}.value'], {
filterFn: ({ parent }) => parent.children.length === 0,
rtn: ({ parents }) => parents.filter((e) => !Array.isArray(e)).map(({ value }) => value).reverse().slice(1).join('')
})(compiled);
};
console.log(parse('abc[de[f,g],hk]'));
// => [ 'abcdef', 'abcdeg', 'abchk' ]
console.log(parse('utvk[fvu,gn[u,k,r],nl,q[t[ij,lo[z,x]],bm]]'));
// => [ 'utvkfvu', 'utvkgnu', 'utvkgnk', 'utvkgnr', 'utvknl', 'utvkqtij', 'utvkqtloz', 'utvkqtlox', 'utvkqbm' ]
</script>
Disclaimer: I'm the author of object-scan

Duplicate Encoder (duplicate letters in a string)

The goal is to convert a string to a new string where each character in the new string is '(' if that character appears only once in the original string, or ')' if that character appears more than once in the original string. Ignore capitalization when determining if a character is a duplicate.
my problem is if it's repeating a letter first parenthese is showing wrong.
function duplicateEncode(word){
var repeat = [];
var result = [];
var letters = word.split('');
for (i=0; i < letters.length; i++){
if (repeat.indexOf(letters[i]) > -1) {
result.push(")");
} else {
result.push("(");
}
repeat.push(letters[i]);
}
return result.join("");
}
console.log(duplicateEncode("aleluia"))
"my problem is if it's repeating a letter first parenthesis is showing wrong."
This is because your code doesn't do any look-ahead, it only checks what characters have already been processed. One way or another you need to check if the current letter also appears earlier or later in the string.
The first way that came to mind was to start by counting all of the letters (putting the counts in an object), then map each letter based on its count. That way you only loop through the original word exactly twice:
function duplicateEncode(word){
var letterCount = {};
var letters = word.toLowerCase().split('');
letters.forEach(function(letter) {
letterCount[letter] = (letterCount[letter] || 0) + 1;
});
return letters.map(function(letter) {
return letterCount[letter] === 1 ? '(' : ')';
}).join('');
}
console.log(duplicateEncode("aleluia"))
console.log(duplicateEncode("AleLuia"))
console.log(duplicateEncode("No duplicates"))
console.log(duplicateEncode("All duplicated ALL DUPLICATED"))
Or the same thing with .reduce() and arrow functions is only three lines:
function duplicateEncode(word){
const letters = word.toLowerCase().split('');
const counts = letters.reduce((ct, ltr) => ((ct[ltr] = (ct[ltr] || 0) + 1), ct), {});
return letters.map(letter => counts[letter] === 1 ? '(' : ')').join('');
}
console.log(duplicateEncode("aleluia"))
console.log(duplicateEncode("AleLuia"))
console.log(duplicateEncode("No duplicates"))
console.log(duplicateEncode("All duplicated ALL DUPLICATED"))
const duplicateEncode = word => {
let newString = ''
word = word.toLowerCase() || word
word.split('').filter((x, index) => {
if(word.indexOf(x) !== index){
newString += ')'
}else if(word.lastIndexOf(x) !== index){
newString += ')'
}else{
newString += '('
}
})
return newString
}
duplicateEncode("O!!!!#k!!!H!!!)!!n!")
You can check the .length of each matched letter in string using RegExp constructor and String.prototype.match(). If .length of matched character is 1 return "(" else return ")"
const word = "aleluia";
let res = [...word].map(letter =>
word.match(new RegExp(letter, "ig")).length === 1 ? "(" : ")"
).join("");
console.log(res);
There is a more simple way to solve this task. Guess it may be more understandable for newbies in JS because the following solution contains only 4 basic level methods. So here we go.
function duplicateEncode(word) {
return word
.toLowerCase()
.split("")
.map(function (a, i, w) {
return w.indexOf(a) == w.lastIndexOf(a) ? "(" : ")";
})
.join("");
}
so:
'a' => '('
'aa' => '))'
'aba' => ')()'
'abA' => ')()'
'aba'
.toLowerCase()
.split('')
.reduce((acc, char, i, arr) => {
const symbol = arr.filter(letter => letter === char).length < 2 ? '(' : ')'
return acc + symbol
}, '')
The reason is your result array is empty until the second iteration (i = 1). The solution is to start with an array with the first element.
function duplicateEncode(word) {
var repeat = [];
var result = [];
var letters = word.split('');
for (i = 0; i < letters.length; i++) {
repeat.push(letters[0]);
if (repeat.indexOf(letters[i]) > -1) {
result.push(")");
} else {
result.push("(");
}
repeat.push(letters[i]);
}
return result.join("");
}
console.log(duplicateEncode("aleluia"))
function duplicateEncode(word){
let w = word.toLowerCase();
return Array.from(w).map(x => w.replace( new RegExp(`[^${x}]`, 'g') , "").length > 1 ? ')' : '(').join('');
}

Comma separated values except for those inside of double quotes [duplicate]

This question already has answers here:
Regex to match all instances not inside quotes
(4 answers)
Closed 7 years ago.
I have a service that formats strings in certain fields. Basically, when a user clicks out of the input box (on blur), the string is cleansed of illegal characters, and whitespace is replaced with commas. This is fine, but I would like to allow a user to add double quotes around grouped words. On blur, this should remove the quotes, but maintain the space in between the words, and then add a comma afterwards. I have tried everything but I can't get this to work. Here is how my service is currently set up:
angular.module('testApp')
.factory('formatStringService', [
function () {
return {
formatString: function (string) {
var styleStr = string;
if (styleStr === undefined) {
return;
}
styleStr = this.stringReplace(styleStr, '\n', ',');
styleStr = this.stringReplace(styleStr, '\t', ',');
styleStr = this.stringReplace(styleStr, ' ', ',');
styleStr = this.stringReplace(styleStr, ';', ',');
styleStr = this.newLine(styleStr);
styleStr = this.validated(styleStr);
for (var g = 0; g < 9; g++) {
styleStr = this.stringReplace(styleStr, ',,', ',');
}
if (styleStr.charAt(styleStr.length - 1) === ',') {
styleStr = styleStr.substr(0, (styleStr.length - 1));
}
if (styleStr.charAt(0) === '*') {
styleStr = styleStr.substr(1, (styleStr.length - 1));
}
if (styleStr.charAt(styleStr.length - 1) === '*') {
styleStr = styleStr.substr(0, (styleStr.length - 1));
}
return styleStr;
},
stringReplace: function (string, text, by) {
var strLength = string.length,
txtLength = text.length;
if ((strLength === 0) || (txtLength === 0)) {
return string;
}
var i = string.indexOf(text);
if ((!i) && (text !== string.substring(0, txtLength))) {
return string;
}
if (i === -1) {
return string;
}
var newstr = string.substring(0, i) + by;
if (i + txtLength < strLength) {
newstr += this.stringReplace(string.substring(i + txtLength, strLength), text, by);
}
return newstr;
},
validated: function (string) {
for (var i = 0, output = '', valid = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,~#+/\\*- '; i < string.length; i++) {
if (valid.indexOf(string.charAt(i)) !== -1) {
output += string.charAt(i);
}
}
return output;
},
newLine: function (string) {
for (var i = 0, output = ''; i < string.length; i++) {
if (string.charCodeAt(i) !== 10) {
output += string.charAt(i);
}
}
return output;
}
};
}
]);
Input string example: 1 2 3 "test test" 7 8
Should output: 1,2,3,test test,7,8
Here's a neat regex trick that you can use for this purpose:
var indices = [],
re = /"[^"]*"|( )/g,
str = '1 2 3 "test test" 7 8';
while ((match = re.exec(str)) !== null) {
if (match[1] !== undefined) indices.push(match.index);
}
var split = [], prevIndex = -1;
indices.forEach(function(index) {
split.push(str.slice(prevIndex + 1, index));
prevIndex = index;
});
document.getElementById('output').innerText = split.join('\n');
<pre id='output'></pre>
What we're doing here is matching on the regex /"[^"]*"|( )/—that is, either "stuff between quotes" or "a single space." So if we find a quote, we immediately start matching "stuff between quotes" (because regex is greedy), and hence any spaces between quotes are just gobbled up in that section of the regex.
Then we know that the ( ) will only be matched if we're not inside double quotes. So we stick the space into a capture group, and then for every match we can simply check whether the capture group exists.
Using a positive look ahead, something like this should do it
'1 2 3 "test test" 7 8'.match(/(".*?"|[^"\s]+)(?=\s|$)/g)
Reg Exp Visualizer

Regex to allow numbers, plus symbol, minus symbol and brackets

I am trying to create a regex that allows only the following 0-9, plus symbol, minus symbol and brackets (). No limitations on length of each of the mentioned. So far I have this but it does not seem to work.
/^[0-9 -+]+$/
Hyphen - has to be at the end of charlist, else it means interval.
/^[0-9 ()+-]+$/
0-9 is possible to write shortly as \d
/^[\d ()+-]+$/
This should work for you:
^[\d\(\)\-+]+$
^ -> start of string
\d -> same as [0-9]
+ -> one or more repetitions
$ -> end of string
DEMO
var re = /^[\d\(\)\-+]+$/m;
var str = ['09+()1213+-','fa(-ds'];
var m;
var result = "";
for(var i = 0; i < str.length; i++) {
if ((m = re.exec(str[i])) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
// View your result using the m-variable.
// eg m[0] etc.
}
result += "\""+str[i]+ "\"" + " is matched:" + (m != null) + "</br>";
}
document.getElementById("results").innerHTML = result
<div id="results"></div>
To match digits, +, -, (, and ) use:
[+()\d-]+
The trick is the position of the characters inside the character class.
if (/^[+()\d-]+$/.test(text)) {
} else {
}
var re = /^[\w\(\)\-\!\+\*\&\%\$#\#\[\]\{\}\<\>\s]+$/m;
var str = ['09+()1213+-[#test#gmail{}<>','fa(-ds'];
var m;
var result = "";
for(var i = 0; i < str.length; i++) {
if ((m = re.exec(str[i])) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
// View your result using the m-variable.
// eg m[0] etc.
}
result += "\""+str[i]+ "\"" + " is matched:" + (m != null) + "</br>";
}
document.getElementById("results").innerHTML = result
<div id="results"></div>
[\d\(\)\+\-\(\)]
That should do it.
EDIT: But since some agree the escaping is too much, here ya go:
[\d+()-]

Categories

Resources