Getting string in quotes in javascript - 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));

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))

Finding the index of several identical words

I have a laTeX string like this
let result = "\\frac{x}{2}+\\frac{3}{x}";
I want to find the index of "frac"s in the string and put them in a array then I want to find the first '}' char after "frac" and replace it with "}/" and finally remove "frac" from the string.
I used this block of code but it just work correctly when we have one "frac"
let result = "\\frac{x}{2}+\\frac{3}{x}";
if (result.indexOf("frac") != -1) {
for (let i = 0; i < result.split("frac").length; i++) {
let j = result.indexOf("frac");
let permission = true;
while (permission) {
if (result[j] == "}") {
result = result.replace(result[j], "}/")
permission = false;
}
j++;
}
result = result.replace('frac', '');
}
}
console.log(result)
OUTPUT: \\{x}//{2}+\\{3}{x}
Could anyone help me to improve my code?
Something like this?
frac(.+?)}
is the literal frac followed by a capture group that will capture one or more of anything .+ until a } and replace it with that anything plus a }/
Using the function replacement to grab index and replace
let result = "\\frac{x}{2}+\\frac{3}{x}";
let pos = [];
const newRes = result.replace(/frac(.+?)}/g,function(match, found, offset,string) {
console.log(match,found,offset,string)
pos.push(offset)
return `${found}/`; // return the found string with the added slash
})
console.log(pos)
console.log(newRes)
Older answer using two sets of code
let result = "\\frac{x}{2}+\\frac{3}{x}";
let re = /frac/gi, res, pos = [];
while ((res = re.exec(result))) {
pos.push(res.index);
}
const newRes = result.replace(/frac(.+?)}/g,"$1}/")
console.log(pos)
console.log(newRes)

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

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)))'

Javascript: Counting frequency of emojis in text

I'm trying to count the frequency of emojis in a block of text. For example:
"I love 🚀🚀🚀 so much 😍 " -> [{🚀:3}, {😍:1}]
In order to count the frequency of characters in a block of text, I'm using
function getFrequency(string) {
var freq = {};
for (var i=0; i<string.length;i++) {
var character = string.charAt(i);
if (freq[character]) {
freq[character]++;
} else {
freq[character] = 1;
}
}
return freq;
};
source: https://stackoverflow.com/a/18619975/4975358
^The above code works great, but it does not recognize emoji characters:
{�: 1, �: 3, �: 2}
Also, I'd prefer the output to be a list of json objects of length 1, as opposed to one long json object.
You can use the callback of the String.replace function and a unicode aware RegExp detecting everything from the unicode blocks "Miscellaneous Symbols" to "Pictographs Transport and Map Symbols" (0x1F300 to 0x1F6FF):
let str = "I love 🚀🚀🚀 so much 😍 ";
let freq = {};
str.replace(/[\u{1F300}-\u{1F6FF}]/gu, char => freq[char] = (freq[char] || 0) + 1);
console.log(freq);
If you prefer to avoid RegExp or String.replace, you can destructure the string into an array and reduce it to the frequencies as follows:
let str = "I love 🚀🚀🚀 so much 😍 ";
let freq = [...str].reduce((freq, char) => {
if (char >= '\u{1F300}' && char < '\u{1F700}') freq[char] = (freq[char] || 0) + 1;
return freq;
}, {});
console.log(freq);
charAt won't help you here. for...of will parse the string correctly into Unicode codepoints including those in the astral plane. We use character.length to determine whether or not this is a supplementary plane character. If you really want to know if it's an emoji, you'd need to tighten this up.
const input = "I love 🚀🚀🚀 so much 😍 ";
function getFrequency(string) {
var freq = {};
for (character of string) {
if (character.length === 1) continue;
if (freq[character]) {
freq[character]++;
} else {
freq[character] = 1;
}
}
return freq;
};
console.log(getFrequency(input));
To create an array of single-valued objects, run the output through this:
function breakProperties(obj) {
return Object.keys(obj).map(function(key) {
var result = {};
result[key] = obj[key];
return result;
});
}

More efficient palindrome code

This is a code I used for the coderbyte challenge "Palindrome". The challenge is to return true if str is the same foward and backward(a palindrome). I got all possible points but I know my code is a little ugly. What would be a more efficient way to write this code. It looks like I am repeating myself and it seems like something that could maybe be written with a for loop.I also see how it could return true when its really false if there was a longer palindrome without the use of a for loop:
function Palindrome(str) {
var low=str.toLowerCase()
var first = low.charAt(0);
var last = low.charAt(low.length-1);
var mid = low.charAt(1);
var mid1 = low.charAt(low.length-2);
if(first===last)
if(mid===mid1)
{
return true
}
else
{
return false
}
else
{
return false
}
}
print(Palindrome(readline()));
To check the string if it's a palindrome you just should compare it to its reversed version.
Say the word hello is not a palndrome because its reversed version olleh is not equal to it. But the word eye is a palindrome same as word abba because they're equal to their reversed versions.
Code example:
(function() {
var reverseStr,
isPalindrome,
testStrings;
reverseStr = function(str) {
var chars = [];
for(var i = str.length - 1; i > -1; i--) {
chars.push(str[i]);
}
return chars.join('');
};
isPalindrome = function(str, ignoreCase) {
if(ignoreCase) {
str = str.toLowerCase();
}
return str === reverseStr(str);
};
testStrings = ['abba', 'hello', 'eye'];
for(var i = 0, l = testStrings.length; i < l; i++) {
var word = testStrings[i];
console.log('Word "%s" is %sa palindrome',
word,
isPalindrome(word) ? '' : 'not ');
}
})();
DEMO #1
Another way that could work faster is listed below. Here you don't receive a reversed string to compare but walking towards the middle of the string from its start and its end.
var isPalindrome = function(str, ignoreCase) {
var length,
last,
halfLength,
i;
if(ignoreCase) {
str = str.toLowerCase();
}
length = str.length;
last = length - 1;
halfLength = Math.ceil(length / 2);
for(i = 0; i < halfLength; i++) {
if(str[i] !== str[last - i]) {
return false;
}
}
return true;
};
DEMO #2
function Palindrome(str) {
str = str.toLowerCase();
str = str.split(" ").join("");
return str == str.split("").reverse().join("");
}
This is what I ended up with. Made sure the string was all lowercase so it wouldn't read a potentially true parameter as false, got rid of the spaces, and then returned true/false based off whether or not the string was equal to it's reverse.
Here is an even easier way:
var isPalindrome = function(string) {
string = string.toLowerCase();
if(string.length===0){
return false;
}
for (var i = 0; i < Math.ceil(string.length/2); i++) {
var j = string.length-1-i;
var character1 = string.charAt(i);
var character2 = string.charAt(j);
if (character1 !== character2) {
return false;
}
}
return true;
};
I came across this palindrome coding challenge with a twist, you have to replace all the non-alphanumeric characters(punctuation, spaces and symbols) and of course change the string into lowercase. This is my solution.
function palindrome(str) {
var low = str.toLowerCase();
var filteredStr = low.replace(/[^0-9a-z]/gi, "");
var split = filteredStr.split("");
var backward = split.reverse();
var join = backward.join("");
if (filteredStr === join) {
return true;
} else {
return false;
}
}
if you care about number of lines of code, here's smaller one
function palindrome(str) {
var low = str.toLowerCase();
var filteredStr = low.replace(/[^0-9a-z]/gi, "");
var backward = filteredStr.split("").reverse().join("");
if (filteredStr === backward) {
return true;
} else {
return false;
}
}
the code is beginner friendly and self explanatory, but if you have any questions regarding the code, feel free to ask ;)

Categories

Resources