How to split a string by AND positioned outside of nested parenthesis? - javascript

Split string with AND which should be outside the round parenthesis and should not match nested parenthesis also.
Ex:
AA:1 AND BB:xyz AND C:(D:1 AND E:23 AND F:(21))
The expected result is:
AA:1
BB:xyz
C:(D:1 AND E:23 AND F:(21))
Ex:
(A:1 AND B:xyz AND C:(D:1 AND E:23 AND F:(21)))
The expected result is:
(A:1 AND B:xyz AND C:(D:1 AND E:23 AND F:(21)))
I tried with AND (?![^(]*)) this regex but this won't work for nested parenthesis.

Not sure how to do this using regex, but using stack it works ( not the most optimised solution though ).
const subjectString = 'A:1 AND B:xyz AND C:(D:1 AND E:23 AND F:(21))'
let bracketStack = []
let splitIndices = []
let result = ''
for (let i = 0; i < subjectString.length; i++) {
switch (subjectString.charAt(i)) {
case '(':
bracketStack.push('(')
break
case ')':
if (!bracketStack.length) {
throw new Error('Invalid Bracket found')
}
bracketStack.pop()
break
default:
if (subjectString.charAt(i) === 'A') {
if (subjectString.charAt(i+1) === 'N' && subjectString.charAt(i+2) === 'D') {
if (!bracketStack.length) {
splitIndices.push(i)
}
i += 2
}
}
}
}
splitIndices.push(-1)
for (let i = 0; i < splitIndices.length; i++) {
let startIndex = 0
let endIndex = splitIndices[i]
if (i !== 0) {
startIndex = splitIndices[i-1] + 3
}
if (splitIndices[i] === -1) {
endIndex = subjectString.length
}
result += subjectString.substring(startIndex, endIndex).trim()
result += '\n'
}
console.log(result)

Not easily possible with JavaScript's somewhat crippled regex engine.
But if you're able to use PCRE (Perl, PHP, Python's regex module), you could split by
(\((?:[^()]*|(?1))+\))(*SKIP)(*FAIL)|AND
which works for arbitrarly nested (but balanced) parentheses.
See a demo on regex101.com.

Used Javascript regex with "negative lookbehind" and split.
The regex:
/(?<!\(.+)\sAND\s/
Javascript regex in testbench and context using split:
const input1 = "AA:1 AND BB:xyz AND C:(D:1 AND E:23 AND F:(21))";
const input2 = "(A:1 AND B:xyz AND C:(D:1 AND E:23 AND F:(21)))";
const regex = /(?<!\(.+)\sAND\s/;
const result1 = getResultAsDecoratedString(input1.split(regex));
alert(result1);
const result2 = getResultAsDecoratedString(input2.split(regex));
alert(result2);
function getResultAsDecoratedString(resultAsArray) {
let result = "";
for (let i = 0; i < resultAsArray.length; i++) {
result += `Group ${i}: '${resultAsArray[i]}'\n`;
}
return result;
}
Output:
Result1:
Group 0: 'AA:1'
Group 1: 'BB:xyz'
Group 2: 'C:(D:1 AND E:23 AND F:(21))'
Result2:
Group 0: '(A:1 AND B:xyz AND C:(D:1 AND E:23 AND F:(21)))'

Related

How could I split an array of words without using split() in javascript

I'm trying to figure out how to split words from a sentence in an array without using split() but using charAt() function.
function sentenceToWordArray(sentence) {
let stringArray = [""]
let j = 0
for (let i = 0; i < sentence.length; i++) {
if (sentence.charAt(i) == " ") {
j++;
stringArray.push("")
} else {
stringArray[j] += sentence.charAt(i)
}
}
return stringArray
}
Now I have the code working but I'm encountering some problems like for example "Hello World" turns into "Hello", "World" but if I add extra spaces for example " Hello World " it outputs ['', 'hello', '', 'there', '']. Is there a way to remove the extra spaces?
If you just want to find all words in an input sentence, you may use match() and avoid splitting altogether:
var input = " Hello World ";
var words = input.match(/\w+/g);
console.log(words);
You can use trim() method, which trims the whitespaces from the start and end of a string
function sentenceToWordArray(sentence) {
let stringArray = [""]
let j = 0
const trimedSentence = sentence.trim()
for (let i = 0; i < trimedSentence.length; i++) {
if (trimedSentence.charAt(i) == " ") {
j++;
stringArray.push("")
} else {
stringArray[j] += trimedSentence.charAt(i)
}
}
return stringArray
}
console.log(sentenceToWordArray('Hello, World'));
console.log(sentenceToWordArray(' Hello, World'));
console.log(sentenceToWordArray('Hello, World '));
Another quickfix:
function sentenceToWordArray(sentence) {
let stringArray = [""]
let j = 0
for (let i = 0; i < sentence.length; i++) {
if (sentence.charAt(i) == " ") {
j++;
stringArray.push("")
} else {
stringArray[j] += sentence.charAt(i)
}
}
return stringArray.filter(w => w.length > 0) // <-- I added this
}
You're going to need something called a "state machine" (https://en.wikipedia.org/wiki/Finite-state_machine). Basically, you have a set of states your function is in. With each new input (=character), depending on the current state, the state is changed into another one, and some actions are performed as a side effect.
Example:
const STATE_BEGIN = 1 // no last character
const STATE_SPACE = 2 // the last character was a space
const STATE_NOSPACE = 3 // the last character was not a space
function split(text) {
let state = STATE_BEGIN
let words = []
for (let char of text) {
let isSpace = char === ' '
switch (state) {
case STATE_BEGIN:
case STATE_SPACE:
if (!isSpace) {
words.push(char)
state = STATE_NOSPACE
} else {
state = STATE_SPACE
}
break;
case STATE_NOSPACE:
if (!isSpace) {
words[words.length - 1] += char
state = STATE_NOSPACE
} else {
state = STATE_SPACE
}
break;
}
}
return words;
}
//
text = ` How razorback-jumping frogs can level six piqued gymnasts!`
console.log(split(text))

Getting string in quotes in javascript

How may I write a function to get all the strings in quotes from a string? The string may contain escaped quotes. I've tried regex but as regex does not have a state like feature, I wasn't able to do that. Example:
apple banana "pear" strawberries "\"tomato\"" "i am running out of fruit\" names here"
should return an array like ['pear', '"tomato"', 'i am running out of fruit" names here']
Maybe something with split can work, though I can't figure out how.
I solved this problem using the following function:
const getStringInQuotes = (text) => {
let quoteTogether = "";
let retval = [];
let a = text.split('"');
let inQuote = false;
for (let i = 0; i < a.length; i++) {
if (inQuote) {
quoteTogether += a[i];
if (quoteTogether[quoteTogether.length - 1] !== '\\') {
inQuote = false;
retval.push(quoteTogether);
quoteTogether = "";
} else {
quoteTogether = quoteTogether.slice(0, -1) + '"'
}
} else {
inQuote = true;
}
}
return retval;
}
Try this:
function getStringInQuotes(text) {
const regex = const regex = /(?<=")\w+ .*(?=")|(?<=")\w+(?=")|\"\w+\"(?=")|(?<=" )\w+(?=")|(?<=")\w+(?= ")/g
return text.match(regex);
}
const text = `apple banana "pear" strawberries "\"tomato\"" "i am running out of fruit\" names here"`;
console.log(getStringInQuotes(text));

How to loop through and segment a string into an array using a RegExp?

I use the custom JS code heavily in Zapier. When arrays are imported into this step, Zapier converts it it into a literal string, i.e:
['BigBoatBob, XL-1', 'LittleBoatMike, M-2', 'SunkBoatCheney, XS-9']
turns into:
'BigBoatBob, XL-1,LittleBoatMike, M-2,SunkBoatCheney, XS-9'
I've created a function to parse out the array items (accounting for textual commas) but it seems very, VERY sloppy. Anyone have any suggestions to refine / shorten/ make look more professional? Thanks for helping me to further my abilities :)
var array = splitArray('BigBoatBob, XL-1,LittleBoatMike, M-2,SunkBoatCheney, XS-9');
function splitArray(x) {
const pos = [];
const POS = [];
const res = [];
for (var i = 0; i < x.length; i++) {
if (x[i] == ',') pos.push(i);
}
for (i = 0; i < pos.length; i++) {
let a = x.slice(pos[i]);
if (!a.startsWith(', ')) POS.push(pos[i]);
}
POS.push(x.length);
POS.unshift(0);
for (i = 0; i < POS.length - 1; i++) {
res.push(x.slice(POS[i], POS[i+1]));
}
return res.map(x => {
if (x.startsWith(',')) {
return x.slice(1);
} else {
return x;
}
});
}
console.log(array);
If you can rely on the spaces after the commas within the strings and rely on their not being one between the strings, you can use split with the regular expression /,(?! )/ which says "a comma not followed by a space:"
const str = 'BigBoatBob, XL-1,LittleBoatMike, M-2,SunkBoatCheney, XS-9';
const array = str.split(/,(?! )/);
console.log(array);
If you can't rely on that but you can rely on the format of the XL-1 and such, you can do it with an exec loop (or with an up-to-date JavaScript engine or a polyfill, with matchAll):
const str = 'BigBoatBob, XL-1,LittleBoatMike, M-2,SunkBoatCheney, XS-9';
const array = [];
const rex = /(.*?,\s*[A-Z]{1,2}-\d)\s*,?/g;
let match;
while ((match = rex.exec(str)) !== null) {
array.push(match[1]);
}
console.log(array);
The regular expression /(.*?,\s*[A-Z]{1,2}-\d)\s*,?/g means:
.*? any number of any character, non-greedy
, a comma
\s* zero or more whitespace characters
[A-Z]{1,2} one or two letters from the range A-Z
- a dash
\d a single digit (use \d+ if there can be more than one)
All of the above is in a capture group
,? an optional comma following it
I would use Array.reduce:
var s = 'BigBoatBob, XL-1,LittleBoatMike, M-2,SunkBoatCheney, XS-9'
var result = s.split(',').reduce((acc, curr, i) => {
if(i % 2 == 0) { acc[i] = curr }
else { acc[i - 1] += curr }
return acc
}, []).filter(x => x)
console.log(result)
Shorthand,
function splitIt(str) {
return str.split(',').reduce((a,v,i)=>((i % 2 == 0)?a.push(v):a[a.length-1]=a[a.length-1]+","+v,a),[]);
}
// Example
let str = `BigBoatBob, XL-1,LittleBoatMike, M-2,SunkBoatCheney, XS-9`;
console.log(splitIt(str));

Return a string with swapping every element[0] by element[1] in a string

I want to swap element 0 by element 1 so element 1 become 0 idx and element 0 become 1 idx.
for instance Hello guys becomes eHllo ugys
my code is kind of tedious and it returns something like this eHll ougys it moves the last letter of a word to the first letter of the next word.
is there a way to do it without forloop?.
const tex = `Hello guys`;
const swap = str => {
let swapped = [];
strin = str.split('');
for (let i = 0; i < strin.length; i++) {
if (i < strin.length) {
swapped[i] = strin[i + 1];
swapped[i + 1] = strin[i];
i += 1;
} else {
swapped[i] = strin[i];
}
}
return swapped.join('');
}
console.log(swap(tex));
One option is to use a regular expression - capture one word character at the beginning of a word followed by another captured word character, and replace with those swapped capture groups:
const tex = `Hello guys`;
const Swap = str => str.replace(/\b(\w)(\w)/g, '$2$1');
console.log(Swap(tex));
This alternative splits the string by space.
Then, using the array, the function map converts the strings into the desired output.
let swap = s =>
s.split(/\s/).map(s => {
let split = s.split(''),
letters = [];
if (split.length > 1) { // This is for string with only one char
// Get the two chars -> He
// reverse them -> eH
letters = split.splice(0, 2).reverse();
}
return letters.join('') + split.join('');
}).join(' ');
console.log(swap("Hello guys"));
console.log(swap("Ele From S"));
You can also do it using split and join (without any regex):
const tex = `Hello guys`;
const strs = tex.split(' ')
const changed = strs.map(str => {
const s = str.split('')
const s1 = s[1]
const s0 = s[0]
s[0] = s1
s[1] = s0
return s.join('')
})
console.log(changed.join(' '))

convert uppercase letters to lower case

I know there are lots of discussions about how to convert characters in a string to all be lower case. My question is why my implementation below is failing.
CODE:
let str = 'aAaA';
const makeLowerCase = (string) => {
for ( var i = 0; i < string.length; i++ ) {
let lower = string[i].toLowerCase();
if (string[i] !== lower ) {
string[i] = lower;
}
}
}
console.log('before', str);
makeLowerCase(str);
console.log('after', str);
Output in console:
before aAaA
after aAaA
Even though for indexes 1 and 3 the if statement test should pass, the code in that block is evidently not running or is running but not having the expected outcome.
Thanks all.
JavaScript strings are immutable. So you can not change the string using C programming style. Rather use the following code
let str = 'aAaA';
const makeLowerCase = (string) => {
for ( var i = 0; i < string.length; i++ ) {
let lower = string[i].toLowerCase();
if (string[i] !== lower ) {
string = string.substring(0,i)+lower+string.substring(i+1);
}
}
return string;
}
console.log('before', str);
str = makeLowerCase(str);
console.log('after', str);
You can also simply call toLowerCase() method on the whole string unless you have any reason to run the loop. Like following:
let str = 'aAaA';
str = str.toLowerCase();
console.log(str);
Close for statement and add the following
Return string
Run
Console.log(makeLowerCase(...))
Or const str = makeLowerCase(...); console.log(str)

Categories

Resources