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 able to successfully target only the letters in my string, however I am having trouble converting only the letters to their unicode values. Please help.
function LetterChanges(str) {
for(var i = 0; i < str.length; i++){
if(str.charCodeAt(i) > 64 && str.charCodeAt(i) < 127){
str.repalce(i, charCodeAt(i));
}
}
console.log(str)
}
LetterChanges("hello*3");
function LetterChanges(str) {
var newStr = ""; // the result string
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c > 64 && c < 127) {
newStr += String.fromCharCode(c + 1);
}
else {
newStr += String.fromCharCode(c);
}
}
return newStr;
}
console.log(LetterChanges("hello*3"));
If you want to replace only alphabetic characters a-z, you can do it using a regular expression like this:
function LetterChanges(str) {
return str.replace(/[a-z]/gi, function(m) {
return String.fromCharCode(
m.charCodeAt(0) + 1
);
});
}
console.log(LetterChanges("Hello*3"));
I actually think this looks a lot better as a one liner. I actually needed to do this in an interview recently and they seemed pretty impressed by the conciseness of my solution
So instead I would use this:
function letterChange(str){
return String.fromCharCode.apply(null,Array.prototype.map.call(str,i=>i.charCodeAt(0)+1));
}
Also not that the i=>u.charCodeAt(0) is a lambda function and will not work in all web browsers without use of a transpiler. You could also use this if you are still using es5:
function letterChange(str){
return String.fromCharCode.apply(null,Array.prototype.map.call(str,function(i){
return i.charCodeAt(0)+1;
}));
}
To explain this a little more lets break it down:
the first thing that may raise questions is the 'apply' function. Apply takes 2 parameters, an object (irrelevant in this case so we use null), and an array of parameters. These parameters are then passed in comma separated form to the function that we call apply on. In the case of fromCharCode, passing multiple parameters returns a String composed of all of the results. Next, we are passing it 'Array.prototype.map.call' which allows us to call the array literal function map on a String which does not come with map out of the box. We then pass this the function to apply and return the full result.
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.
My whole goal was to write a loop that would take a string, count the letters and return two responses: one = "this word is symmetric" or two = "this word is not symmetric". However the code I wrote doesn't console anything out. Here's the code:
var arya = function(arraycount){
for (arraycount.length >= 1; arraycount.length <= 100; arraycount++) {
while (arraycount.length%2 === 0) {
console.log("This is a symmetric word and its length is " + " " arraycount.length " units.");
arraycount.length%2 != 0
console.log("Not a symmetric word");
}
}
}
arya("Michael");
There are many ways to accomplish your goal, but here are a few. The first is a somewhat naïve approach using a for loop, and the second uses recursion. The third asks whether the string equals the reverse of the string.
iterative (for loop) function
var isPalindromeIteratively = function(string) {
if (string.length <= 1) {
return true;
}
for (var i = 0; i <= Math.floor(string.length / 2); i++) {
if (string[i] !== string[string.length - 1 - i]) {
return false;
}
}
return true;
};
This function begins by asking whether your input string is a single character or empty string, in which case the string would be a trivial palindrome. Then, the for loop is set up: starting from 0 (the first character of the string) and going to the middle character, the loop asks whether a given character is identical to its partner on the other end of the string. If the parter character is not identical, the function returns false. If the for loop finishes, that means every character has an identical partner, so the function returns true.
recursive function
var isPalindromeRecursively = function(string) {
if (string.length <= 1) {
console.log('<= 1');
return true;
}
var firstChar = string[0];
var lastChar = string[string.length - 1];
var substring = string.substring(1, string.length - 1);
console.log('first character: ' + firstChar);
console.log('last character: ' + lastChar);
console.log('substring: ' + substring);
return (firstChar === lastChar) ? isPalindromeRecursively(substring) : false;
};
This function begins the same way as the first, by getting the trivial case out of the way. Then, it tests whether the first character of the string is equal to the last character. Using the ternary operator, the function, returns false if that test fails. If the test is true, the function calls itself again on a substring, and everything starts all over again. This substring is the original string without the first and last characters.
'reflecting' the string
var reflectivePalindrome = function(string) {
return string === string.split('').reverse().join('');
};
This one just reverses the string and sees if it equals the input string. It relies on the reverse() method of Array, and although it's the most expressive and compact way of doing it, it's probably not the most efficient.
usage
These will return true or false, telling you whether string is a palindrome. I assumed that is what you mean when you say "symmetric." I included some debugging statements so you can trace this recursive function as it works.
The Mozilla Developer Network offers a comprehensive guide of the JavaScript language. Also, here are links to the way for loops and while loops work in JS.
The Javascript splice only works with arrays. Is there similar method for strings? Or should I create my own custom function?
The substr(), and substring() methods will only return the extracted string and not modify the original string. What I want to do is remove some part from my string and apply the change to the original string. Moreover, the method replace() will not work in my case because I want to remove parts starting from an index and ending at some other index, exactly like what I can do with the splice() method. I tried converting my string to an array, but this is not a neat method.
It is faster to slice the string twice, like this:
function spliceSlice(str, index, count, add) {
// We cannot pass negative indexes directly to the 2nd slicing operation.
if (index < 0) {
index = str.length + index;
if (index < 0) {
index = 0;
}
}
return str.slice(0, index) + (add || "") + str.slice(index + count);
}
than using a split followed by a join (Kumar Harsh's method), like this:
function spliceSplit(str, index, count, add) {
var ar = str.split('');
ar.splice(index, count, add);
return ar.join('');
}
Here's a jsperf that compares the two and a couple other methods. (jsperf has been down for a few months now. Please suggest alternatives in comments.)
Although the code above implements functions that reproduce the general functionality of splice, optimizing the code for the case presented by the asker (that is, adding nothing to the modified string) does not change the relative performance of the various methods.
Edit
This is of course not the best way to "splice" a string, I had given this as an example of how the implementation would be, which is flawed and very evident from a split(), splice() and join(). For a far better implementation, see Louis's method.
No, there is no such thing as a String.splice, but you can try this:
newStr = str.split(''); // or newStr = [...str];
newStr.splice(2,5);
newStr = newStr.join('');
I realise there is no splice function as in Arrays, so you have to convert the string into an array. Hard luck...
Here's a nice little Curry which lends better readability (IMHO):
The second function's signature is identical to the Array.prototype.splice method.
function mutate(s) {
return function splice() {
var a = s.split('');
Array.prototype.splice.apply(a, arguments);
return a.join('');
};
}
mutate('101')(1, 1, '1');
I know there's already an accepted answer, but hope this is useful.
There seem to be a lot of confusion which was addressed only in comments by elclanrs and raina77ow, so let me post a clarifying answer.
Clarification
From "string.splice" one may expect that it, like the one for arrays:
accepts up to 3 arguments: start position, length and (optionally) insertion (string)
returns the cut out part
modifies the original string
The problem is, the 3d requirement can not be fulfilled because strings are immutable (related: 1, 2), I've found the most dedicated comment here:
In JavaScript strings are primitive value types and not objects (spec). In fact, as of ES5, they're one of the only 5 value types alongside null, undefined, number and boolean. Strings are assigned by value and not by reference and are passed as such. Thus, strings are not just immutable, they are a value. Changing the string "hello" to be "world" is like deciding that from now on the number 3 is the number 4... it makes no sense.
So, with that in account, one may expect the "string.splice" thing to only:
accepts up to 2 arguments: start position, length (insertion makes no sense since the string is not changed)
returns the cut out part
which is what substr does; or, alternatively,
accepts up to 3 arguments: start position, length and (optionally) insertion (string)
returns the modified string (without the cut part and with insertion)
which is the subject of the next section.
Solutions
If you care about optimizing, you should probably use the Mike's implementation:
String.prototype.splice = function(index, count, add) {
if (index < 0) {
index += this.length;
if (index < 0)
index = 0;
}
return this.slice(0, index) + (add || "") + this.slice(index + count);
}
Treating the out-of-boundaries index may vary, though. Depending on your needs, you may want:
if (index < 0) {
index += this.length;
if (index < 0)
index = 0;
}
if (index >= this.length) {
index -= this.length;
if (index >= this.length)
index = this.length - 1;
}
or even
index = index % this.length;
if (index < 0)
index = this.length + index;
If you don't care about performance, you may want to adapt Kumar's suggestion which is more straight-forward:
String.prototype.splice = function(index, count, add) {
var chars = this.split('');
chars.splice(index, count, add);
return chars.join('');
}
Performance
The difference in performances increases drastically with the length of the string. jsperf shows, that for strings with the length of 10 the latter solution (splitting & joining) is twice slower than the former solution (using slice), for 100-letter strings it's x5 and for 1000-letter strings it's x50, in Ops/sec it's:
10 letters 100 letters 1000 letters
slice implementation 1.25 M 2.00 M 1.91 M
split implementation 0.63 M 0.22 M 0.04 M
note that I've changed the 1st and 2d arguments when moving from 10 letters to 100 letters (still I'm surprised that the test for 100 letters runs faster than that for 10 letters).
I would like to offer a simpler alternative to both the Kumar/Cody and the Louis methods. On all the tests I ran, it performs as fast as the Louis method (see fiddle tests for benchmarks).
String.prototype.splice = function(startIndex,length,insertString){
return this.substring(0,startIndex) + insertString + this.substring(startIndex + length);
};
You can use it like this:
var creditCardNumber = "5500000000000004";
var cardSuffix = creditCardNumber.splice(0,12,'****');
console.log(cardSuffix); // output: ****0004
See Test Results:
https://jsfiddle.net/0quz9q9m/5/
Simply use substr for string
ex.
var str = "Hello world!";
var res = str.substr(1, str.length);
Result = ello world!
The method Louis's answer, as a String prototype function:
String.prototype.splice = function(index, count, add) {
if (index < 0) {
index = this.length + index;
if (index < 0) {
index = 0;
}
}
return this.slice(0, index) + (add || "") + this.slice(index + count);
}
Example:
> "Held!".splice(3,0,"lo Worl")
< "Hello World!"
So, whatever adding splice method to a String prototype cant work transparent to spec...
Let's do some one extend it:
String.prototype.splice = function(...a){
for(var r = '', p = 0, i = 1; i < a.length; i+=3)
r+= this.slice(p, p=a[i-1]) + (a[i+1]||'') + this.slice(p+a[i], p=a[i+2]||this.length);
return r;
}
Every 3 args group "inserting" in splice style.
Special if there is more then one 3 args group, the end off each cut will be the start of next.
'0123456789'.splice(4,1,'fourth',8,1,'eighth'); //return '0123fourth567eighth9'
You can drop or zeroing the last arg in each group (that treated as "nothing to insert")
'0123456789'.splice(-4,2); //return '0123459'
You can drop all except 1st arg in last group (that treated as "cut all after 1st arg position")
'0123456789'.splice(0,2,null,3,1,null,5,2,'/',8); //return '24/7'
if You pass multiple, you MUST check the sort of the positions left-to-right order by youreself!
And if you dont want you MUST DO NOT use it like this:
'0123456789'.splice(4,-3,'what?'); //return "0123what?123456789"
Louis's spliceSlice method fails when add value is 0 or other falsy values, here is a fix:
function spliceSlice(str, index, count, add) {
if (index < 0) {
index = str.length + index;
if (index < 0) {
index = 0;
}
}
const hasAdd = typeof add !== 'undefined';
return str.slice(0, index) + (hasAdd ? add : '') + str.slice(index + count);
}
I solved my problem using this code, is a somewhat replacement for the missing splice.
let str = "I need to remove a character from this";
let pos = str.indexOf("character")
if(pos>-1){
let result = str.slice(0, pos-2) + str.slice(pos, str.length);
console.log(result) //I need to remove character from this
}
I needed to remove a character before/after a certain word, after you get the position the string is split in two and then recomposed by flexibly removing characters using an offset along pos