So I'm trying to parse specific sequence of string in JavaScript. Basically i need to parse (123 abc)(()) where () is like openning array and closing, and values in the array would be split by spaces.
For example:
(133 abs)(()) will give me [["123", "abs"],[[]]]
No JQuery or ragex,only vanilla JavaScript
i tried doing but it errors with recursion error, can't really send the try due it was on another computer
Does this do what you need? No RegExp. No JQuery.
This splits the input string into codepoints and recursively enumerates that array.
The base recursive step is to check if we have reached the end of the codepoint array, at which point we exit the recursion.
For each codepoint we use a switch statement to choose the value to insert into the result depending on the current codepoint, the previous codepoint, and whether we are inside an "item".
Codepoint by codepoint a result string is constructed. This code will probably break for Unicode strings containing grapheme clusters.
const transform = (str) => {
let result = []
let withinItem = false
const arr = [...str] // split into codepoints
const next = (arr, pos, prev = null) => {
if(pos >= arr.length) return
let curr = arr[pos]
switch(true) {
case curr === '(':
prev === ')' && result.push(',')
result.push('[')
break
case curr === ' ':
result.push('", ')
withinItem = false
break
case curr === ')':
withinItem && result.push('"')
result.push(']')
withinItem = false
break
default:
!withinItem && result.push('"')
result.push(curr)
withinItem = true
break
}
next(arr, ++pos, curr)
}
next(arr, 0)
return `[${result.join('')}]`
}
console.log(transform('(133 abs)(())'))
Related
I have a javascript problem that I am very confused about
Write a function stringCase that takes a string with mixed uppercase and lowercase characters and return the string in either all uppercase or all lowercase depending on which letter case the string has more of. If the string has equal upper and lower case characters, convert the string to all lowercase. Do not include any loops, length property, or native methods (exception: toLowerCase() & toUpperCase() allowed).
I can immediately think of a few approaches to this problem, like using a loop to check each character, or using a replace method and regex and getting the length of the result. However, the prompt prohibits me from using loops, length property, and native methods.
Any help would be appreciated. Thank you in advance!
The trick here is to use recursion to do the loop. You can manually track the pointer and counts.
const stringCase = (s) => {
let i = 0;
let count = 0;
const process = () => {
const curr = s[i++];
if (!curr) {
return count <= 0 ? s.toLowerCase() : s.toUpperCase();
}
if (curr.toLowerCase() === curr) {
count--
} else {
count++
}
return process();
}
return process();
};
console.log(stringCase('Aa') === 'aa');
console.log(stringCase('AaA') === 'AAA');
console.log(stringCase('aa') === 'aa');
console.log(stringCase('AA') === 'AA');
So my answer is similar to JBallin's, except that I used a frequency counter to keep track of the upper and lower case counts. This reduces the amount of moving parts for the helper function.
function convertString(str) {
// object to keep track frequency of upper/lower case letters
const frequencyCounter = {
upper: 0,
lower: 0
}
let currentIndex = 0
// helper function to increment frequencies of letters
function checkCase() {
const currentChar = str[currentIndex]
// if current index is outside length of string and returns undefined
// break recursive loop by returning
if (!currentChar) return
if (currentChar.toUpperCase() === currentChar) {
frequencyCounter.upper +=1;
} else {
frequencyCounter.lower +=1;
}
// increment current index and recursively call helper function
currentIndex++
return checkCase()
}
// start helper function
checkCase()
// check for which case to convert the string to and return that string
if (frequencyCounter.upper > frequencyCounter.lower) {
return str.toUpperCase();
} else {
return str.toLowerCase();
}
}
I'm working with words and their phonemes. I found that in my code (and in the console) what looks like two identical strings " 'b eh1 r z'" for example are not returning true when compared, whether with double or triple equals. I did sanity tests in the chrome console, in the node console and in the file itself, and they all return expected results (i.e. only the 'strinfigied' variable seems corrupted. I'm racking my brains trying to figure what's going on. This is what is not workign as expected:
let stringified = trialPhonemeSequence.join(" ")
if (p == "z"){
console.log(trialPhonemeSequence)
let bearstring = 'b eh1 r z'
console.log("SHould be adding 'z' at ", i, "so we got", trialPhonemeSequence, "and stringified", stringified)
console.log(`String|${dictionary['bears']}| length ${dictionary['bears'].length} should equal |${stringified}| length ${stringified.length}: ${dictionary['bears'] == stringified} and ${bearstring == stringified}`);
}
What the Chrome Console outputs
String|b eh1 r z| length 10 should equal |b eh1 r z| length 10: false and false
Here is the entire function up to that point for context. I don't think you want the entire min reproduable code as it requires large dictionaries and datasets and initialization. The goal of this function was to input bear and look for words that are a phonemic match, allowing for addition of a phoneme (the 'z' sound in this test case).
function findAddedPhonemes(word, dictionary, hashMap)
{
let matches = []
let phonemeSequence = dictionary[word]
let phonemeSequenceList = phonemeSequence.split(" ")
for (let i = 0; i <= phonemeSequenceList.length; i++)
{
phonemeList.forEach((p, ind) => // all the items in the list
{
let trialPhonemeSequence = phonemeSequenceList.slice()
trialPhonemeSequence.splice(i, 0, p) // insert p phoneme into index
let stringified = trialPhonemeSequence.join(" ")
if (p == "z"){
console.log(trialPhonemeSequence)
let bearstring = 'b eh1 r z'
console.log(`String|${dictionary['bears']}| length ${dictionary['bears'].length} should equal |${stringified}| length ${stringified.length}: ${dictionary['bears'] == stringified} and ${bearstring == stringified}`);
}
if (stringified == "b eh1 r z"){ //THIS IS WHERE ITS BROKEN
console.log("Bears stringified searching!!!!!!!!!!!!")
}
let hash = stringified.hashCode(dictKeys.length * 4)
if (hashMap[hash] !== undefined)
{
hashMap[hash].forEach((o) =>
{
let key = getObjectsKey(o)
if (checkIfIdentical(dictionary[key], stringified))
{
matches.push(key)
}
})
}
})
}
console.log("Matches", matches)
return matches
}
EDIT (SOLVED):
There is a char 13 (Carriage Return) in Stringified string but not the others. I think I understand where this is coming from. I was inserting a new phoneme with splice in each syllable of the word, and when splicing it onto the end of the words, it's not automatically stripping the '\n', which results in comparison errors. I now know one has to do this manually and wrong hash values. BTW the phoneme dictionary ishere
Thanks #VLAZ !
stringified.split("").map(c => {
console.log(c.charCodeAt(0))
})
console.log("New word")
bearstring.split("").map(c => {
console.log(c.charCodeAt(0))
})
console.log(stringified==bearstring)
I found myself needing to do camel case to sentence case string conversion with sane acronym support, a google search for ideas led me to the following SO post:
Convert camelCaseText to Sentence Case Text
Which is actually asking about title case not sentence case so I came up with the following solution which maybe others will find helpful or can offer improvements to, it is using ES6 which is acceptable for me and can easily be polyfilled if there's some horrible IE requirement.
The below uses capitalised notation for acronyms; I don't agree with Microsoft's recommendation of capitalising when more than two characters so this expects the whole acronym to be capitalised even if it's at the start of the string (which technically means it's not camel case but it gives sane controllable output), multiple consecutive acronyms can be escaped with _ (e.g. parseDBM_MXL -> Parse DBM XML).
function camelToSentenceCase(str) {
return str.split(/([A-Z]|\d)/).map((v, i, arr) => {
// If first block then capitalise 1st letter regardless
if (!i) return v.charAt(0).toUpperCase() + v.slice(1);
// Skip empty blocks
if (!v) return v;
// Underscore substitution
if (v === '_') return " ";
// We have a capital or number
if (v.length === 1 && v === v.toUpperCase()) {
const previousCapital = !arr[i-1] || arr[i-1] === '_';
const nextWord = i+1 < arr.length && arr[i+1] && arr[i+1] !== '_';
const nextTwoCapitalsOrEndOfString = i+3 > arr.length || !arr[i+1] && !arr[i+3];
// Insert space
if (!previousCapital || nextWord) v = " " + v;
// Start of word or single letter word
if (nextWord || (!previousCapital && !nextTwoCapitalsOrEndOfString)) v = v.toLowerCase();
}
return v;
}).join("");
}
// ----------------------------------------------------- //
var testSet = [
'camelCase',
'camelTOPCase',
'aP2PConnection',
'JSONIsGreat',
'thisIsALoadOfJSON',
'parseDBM_XML',
'superSimpleExample',
'aGoodIPAddress'
];
testSet.forEach(function(item) {
console.log(item, '->', camelToSentenceCase(item));
});
I am sure there is probably a dupe of this here somewhere, but if so I cannot seem to find it, nor can I glue the pieces together correctly from what I could find to get what I need. I am using JavaScript and need the following:
1) Replace the first character of a string with it's Unicode aware capitalization UNLESS the next (second) character is a - OR ` or ' (minus/dash, caret, or single-quote).
I have come close with what I could find except for getting the caret and single quote included (assuming they need to be escaped somehow) and what I believe to be a scope issue with the following because first returns undefined. I am also not positive which JS/String functions are Unicode aware:
autoCorrect = (str) => {
return str.replace(/^./, function(first) {
// if next char is not - OR ` OR ' <- not sure how to handle caret and quote
if(str.charAt(1) != '-' ) {
return first.toUpperCase(); // first is undefined here - scope??
}
});
}
Any help is appreciated!
Internally, JavaScript uses UCS-2, not UTF-8.
Handling Unicode in JavaScript isn't particularly beautiful, but possible. It becomes particularly ugly with surrogate pairs such as "🐱", but the for..of loop can handle that. Do never try to use indices on Unicode strings, as you might get only one half of a surrogate pair (which breaks Unicode).
This should handle Unicode well and do what you want:
function autoCorrect(string) {
let i = 0, firstSymbol;
const blacklist = ["-", "`", "'"];
for (const symbol of string) {
if (i === 0) {
firstSymbol = symbol;
}
else if (i === 1 && blacklist.some(char => char === symbol)) {
return string;
}
else {
const rest = string.substring(firstSymbol.length);
return firstSymbol.toUpperCase() + rest;
}
++i;
}
return string.toUpperCase();
}
Tests
console.assert(autoCorrect("δα") === "Δα");
console.assert(autoCorrect("🐱") === "🐱");
console.assert(autoCorrect("d") === "D");
console.assert(autoCorrect("t-minus-one") === "t-minus-one");
console.assert(autoCorrect("t`minus`one") === "t`minus`one");
console.assert(autoCorrect("t'minus'one") === "t'minus'one");
console.assert(autoCorrect("t^minus^one") === "T^minus^one");
console.assert(autoCorrect("t_minus_one") === "T_minus_one");
I've been on this problem for several hours now and have done all I can to the best of my current newbie javaScript ability to solve this challenge but I just can't figure out exactly what's wrong. I keep getting "UNEXPECTED TOKEN ILLEGAL on here: http://jsfiddle.net/6n8apjze/14/
and "TypeError: Cannot read property 'length' of null": http://goo.gl/LIz89F
I think the problem is the howManyRepeat variable. I don't understand why I'm getting it can't read the length of null when clearly word is a word from str...
I got the idea for:
word.toLowerCase().split("").sort().join("").match(/([.])\1+/g).length
...here: Get duplicate characters count in a string
The Challenge:
Using the JavaScript language, have the function LetterCountI(str) take the str
parameter being passed and return the first word with the greatest number of
repeated letters. For example: "Today, is the greatest day ever!" should return
greatest because it has 2 e's (and 2 t's) and it comes before ever which also
has 2 e's. If there are no words with repeating letters return -1. Words will
be separated by spaces.
function LetterCountI(str){
var wordsAndAmount={};
var mostRepeatLetters="-1";
var words=str.split(" ");
words.forEach(function(word){
// returns value of how many repeated letters in word.
var howManyRepeat=word.toLowerCase().split("").sort().join("").match(/([.])\1+/g).length;
// if there are repeats(at least one value).
if(howManyRepeat !== null || howManyRepeat !== 0){
wordsAndAmount[word] = howManyRepeat;
}else{
// if no words have repeats will return -1 after for in loop.
wordsAndAmount[word] = -1;
}
});
// word is the key, wordsAndAmount[word] is the value of word.
for(var word in wordsAndAmount){
// if two words have same # of repeats pick the one before it.
if(wordsAndAmount[word]===mostRepeatLetters){
mostRepeatLetters=mostRepeatLetters;
}else if(wordsAndAmount[word]<mostRepeatLetters){
mostRepeatLetters=mostRepeatLetters;
}else if(wordsAndAmount[word]>mostRepeatLetters){
mostRepeatLetters=word;
}
}
return mostRepeatLetters;
}
// TESTS
console.log("-----");
console.log(LetterCountI("Today, is the greatest day ever!"));
console.log(LetterCountI("Hello apple pie"));
console.log(LetterCountI("No words"));
Any guidance is much appreciated. Thank you!! ^____^
Here is the working code snippet:
/*
Using the JavaScript language, have the function LetterCountI(str) take the str
parameter being passed and return the first word with the greatest number of
repeated letters. For example: "Today, is the greatest day ever!" should return
greatest because it has 2 e's (and 2 t's) and it comes before ever which also
has 2 e's. If there are no words with repeating letters return -1. Words will
be separated by spaces.
console.log(LetterCountI("Today, is the greatest day ever!") === "greatest");
console.log(LetterCountI("Hello apple pie") === "Hello");
console.log(LetterCountI("No words") === -1);
Tips:
This is an interesting problem. What we can do is turn the string to lower case using String.toLowerCase, and then split on "", so we get an array of characters.
We will then sort it with Array.sort. After it has been sorted, we will join it using Array.join. We can then make use of the regex /(.)\1+/g which essentially means match a letter and subsequent letters if it's the same.
When we use String.match with the stated regex, we will get an Array, whose length is the answer. Also used some try...catch to return 0 in case match returns null and results in TypeError.
/(.)\1+/g with the match method will return a value of letters that appear one after the other. Without sort(), this wouldn't work.
*/
function LetterCountI(str){
var wordsAndAmount={};
var mostRepeatLetters="";
var words=str.split(" ");
words.forEach(function(word){
var howManyRepeat=word.toLowerCase().split("").sort().join("").match(/(.)\1+/g);
if(howManyRepeat !== null && howManyRepeat !== 0){ // if there are repeats(at least one value)..
wordsAndAmount[word] = howManyRepeat;
} else{
wordsAndAmount[word] = -1; // if no words have repeats will return -1 after for in loop.
}
});
// console.log(wordsAndAmount);
for(var word in wordsAndAmount){ // word is the key, wordsAndAmount[word] is the value of word.
// console.log("Key = " + word);
// console.log("val = " + wordsAndAmount[word]);
if(wordsAndAmount[word].length>mostRepeatLetters.length){ //if two words have same # of repeats pick the one before it.
mostRepeatLetters=word;
}
}
return mostRepeatLetters ? mostRepeatLetters : -1;
}
// TESTS
console.log("-----");
console.log(LetterCountI("Today, is the greatest day ever!"));
console.log(LetterCountI("Hello apple pie"));
console.log(LetterCountI("No words"));
/*
split into words
var wordsAndAmount={};
var mostRepeatLetters=0;
loop through words
Check if words has repeated letters, if so
Push amount into object
Like wordsAndAmount[word[i]]= a number
If no repeated letters...no else.
Loop through objects
Compare new words amount of repeated letters with mostRepeatLetters replacing whoever has more.
In the end return the result of the word having most repeated letters
If all words have no repeated letters return -1, ie.
*/
The changes made:
[.] turned into . as [.] matches a literal period symbol, not any character but a newline
added closing */ at the end of the code (the last comment block was not closed resulting in UNEXPECTED TOKEN ILLEGAL)
if(howManyRepeat !== null || howManyRepeat !== 0) should be replaced with if(howManyRepeat !== null && howManyRepeat !== 0) since otherwise the null was testing for equality with 0 and led to the TypeError: Cannot read property 'length' of null" issue. Note that .match(/(.)\1+/g).length cannot be used since the result of matching can be null, and this will also cause the TypeError to appear.
The algorithm for getting the first entry with the greatest number of repetitions was wrong since the first if block allowed subsequent entry to be output as a correct result (not the first, but the last entry with the same repetitions was output actually)
-1 can be returned if mostRepeatLetters is empty.
Hope you dont mind if I rewrite this code. My code may not be that efficient.
Here is a snippet
function findGreatest() {
// ipField is input field
var getString = document.getElementById('ipField').value.toLowerCase();
var finalArray = [];
var strArray = [];
var tempArray = [];
strArray = (getString.split(" "));
// Take only those words which has repeated letter
for (var i = 0, j = strArray.length; i < j; i++) {
if ((/([a-zA-Z]).*?\1/).test(strArray[i])) {
tempArray.push(strArray[i]);
}
}
if (tempArray.length == 0) { // If no word with repeated Character
console.log('No such Word');
return -1;
} else { // If array has words with repeated character
for (var x = 0, y = tempArray.length; x < y; x++) {
var m = findRepWord(tempArray[x]); // Find number of repeated character in it
finalArray.push({
name: tempArray[x],
repeat: m
})
}
// Sort this array to get word with largest repeated chars
finalArray.sort(function(z, a) {
return a.repeat - z.repeat
})
document.getElementById('repWord').textContent=finalArray[0].name;
}
}
// Function to find the word which as highest repeated character(s)
function findRepWord(str) {
try {
return str.match(/(.)\1+/g).length;
} catch (e) {
return 0;
} // if TypeError
}
Here is DEMO
function LetterCountI(str) {
var word_arr = str.split(" ");
var x = word_arr.slice();
for(var i = 0; i < x.length; i ++){
var sum = 0;
for(var y = 0; y < x[i].length; y++){
var amount = x[i].split("").filter(function(a){return a == x[i][y]}).length;
if (amount > 1){
sum += amount
}
}
x[i] = sum;
}
var max = Math.max.apply(Math,x);
if(max == 0)
return -1;
var index = x.indexOf(max);
return(word_arr[index]);
};
Here is another version as well.
You could use new Set in the following manner:
const letterCount = s => {
const res = s.split(' ')
.map(s => [s, (s.length - new Set([...s]).size)])
.reduce((p, c) => (!p.length) ? c
: (c[1] > p[1]) ? c : p, []);
return !res[1] ? -1 : res.slice(0,1).toString()
}
Note: I have not tested this solution (other than the phrases presented here), but the idea is to subtract unique characters from the total characters in each word of the phrase.