Array not updated when iterating with "let element of array" - javascript

I fail to see why the first code below(titleCase1) does not capitalize every word, while the second one does(titleCase2).
var result1 = titleCase1('this is a new question in stackoverflow');
console.log('result1:',result1);
var result2 = titleCase2('this is a new question in stackoverflow');
console.log('result2:',result2);
function titleCase1(str) {
let words = str.split(" ");
for (let word of words) {
word = word[0].toUpperCase() + word.slice(1);
}
return words.join(" ");
}
function titleCase2(str) {
let words = str.split(" ");
for (let i = 0; i < words.length; i++) {
words[i] = words[i][0].toUpperCase() + words[i].slice(1);
}
return words.join(" ");
}
It seems that in the first case the words array is not updated and it has something to do with the let element of array iterator, but I do not understand why it does not work.

Strings, unlike arrays in JavaScript are value objects, not reference objects.
Here:
for (let word of words) {
word = word[0].toUpperCase() + word.slice(1);
}
You are declaring a word variable using let and it is scoped to the for loop. This variable is a copy of the substrings in your string. You reassign it at each iteration, but since it is a copy and not a reference to the substrings, your substring in the array words doesn't change, only the copy does.
However here:
for (let i = 0; i < words.length; i++) {
words[i] = words[i][0].toUpperCase() + words[i].slice(1);
}
You are changing the substring directly since you update each character by indexing them in the substring array.
Here is a much shorter way to do it with String.replace, a regex and an arrow function:
const titleCase = str => str.replace(/(?<=(\s+|^))\w/gi, x => x.toUpperCase());
console.log(titleCase('hello world'));
console.log(titleCase(' hello world'));
The regex (?<=(\s+|^)) is a positive lookbehind and makes sure that the pattern \w (word character) is preceeded by spaces or is located at the beginning of the string.

In the first code, you're reassigning a variable. Reassigning a variable, by itself, will never have any effect on anything else, at least in 99% of situations; it'll just mean that further references to word inside that for block will refer to the new value, rather than the old value. So, your word = ... won't affect anything, since you're not doing anything with that new word variable name later in the block. (after that iteration ends, the value stored in it will be unreferenced, and will soon be GC'd)
In the second code, you're mutating an object: words[i] = will mean that further accesses to index i of words will return the new value.

word is not an array. It is just a word. and you are returning words with joining them which is already a string with no space join will have no effect
function titleCase(str) {
let a=[];
let words = str.split("");
for (let word of words) {
a.push(word.toUpperCase());
}
return a.join("");
}
console.log(titleCase("hello"))

Related

Finding longest word in a string javascript code that is not working on the last wordd

I've already solved this by using a split function but I'm still confused as to why my previous for-loop only code is not working when I'm trying to find the longest word in a string in javascript. The function I'm writing is supposed to return the number of letters of the longest word. After using the console, whenever i use only one word, it returns the value i initialized the counter of the letters with (which is 0). If the longest word is the last word, it returns the second longest word on the preceding words. If the longest word is on anywhere except the last word, the result is accurate. It seems like it has trouble counting the letters of the last word. Here is my code.
let lettCount = 0;
let largest = 0;
let spaceCheck = /\s/;
for(let i = 0; i < str.length; i++) {
if(spaceCheck.test(str[i]) == true) {
if(lettCount > largest) {
largest = lettCount;
}
lettCount = 0;
} else {
lettCount++;
}
}
return largest;
You should be able to simplify the logic here significantly, using String.split() and Math.max().
We split the string into an array, then use Array.map() to get the length of each word, then use Math.max() along with the Spread syntax to get the longest word.
let str = 'the longest word is dinosaur';
function getLongest(str) {
return Math.max(...str.split(/\s/).map(s => s.length));
}
console.log('Longest word:', getLongest(str));
You can also do this with String.split() and Array.reduce(), this is even a little simpler:
let str = 'the longest word is dinosaur';
function getLongest(str) {
return str.split(/\s/).reduce((acc,s) => s.length > acc ? s.length: acc, 0);
}
console.log('Longest word:', getLongest(str));

Why does toLowerCase() does not work with replace()?

Given Below is a simple function to capitalize the first words in a sentence
Eg: INPUT: 'JavaSCRipt is The BEST'
OUTPUT: 'JavaScript Is The Best'
const firstupper = function(str){
const arr = str.split(' ');
const newArr = [];
for(let item of arr){
item = item.toLowerCase();
newArr.push(item.replace(item[0], item[0].toUpperCase()));
}
const newstr = newArr.join(' ');
console.log(newstr);
}
firstupper('javaSCript is THE besT');
P.S -- This code works fine
Why can't I make to lower case and then replace the first letter in upper case in single line
like : newArr.push(item.toLowerCase().replace(item[0], item[0].toUpperCase()));
When I write the code using this it is changing the first word to lower if it is in upper case vice versa
Eg: INPUT -> 'JAvaScript is The best'
OUTPUT - > 'javascript Is the Best'
Because that changes the logic. In this version, all reads of item in the .push() operation are lower-cased:
item = item.toLowerCase();
newArr.push(item.replace(item[0], item[0].toUpperCase()));
But in this version, only the first use of item is lower-cased:
newArr.push(item.toLowerCase().replace(item[0], item[0].toUpperCase()));
The references to item[0] both still use whatever the original casing was. To make it the same logic you'd need to repeat the case change there as well:
newArr.push(item.toLowerCase().replace(item.toLowerCase()[0], item.toLowerCase()[0].toUpperCase()));
Which clearly is too cluttered and unnecessarily repeats the operation. So the original working version is preferred.
This could help
const str = 'JavaSCRipt is The BEST';
//split the above string into an array of strings
//whenever a blank space is encountered
const arr = str.split(" ");
//loop through each element of the array and capitalize the first letter.
for (var i = 0; i < arr.length; i++) {
arr[i] = arr[i].toLowerCase();
arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].slice(1);
}
//Join all the elements of the array back into a string
//using a blankspace as a separator
const str2 = arr.join(" ");
console.log(str2);
//Outptut: Javascript Is The Best

Write a method that takes in a sentence string and returns a new sentence representing its Aba translation

Beginner Coder and I'm confused if my include method is wrong. Maybe I should create an array and push the characters in? Am I on the right track?
Aba is a German children's game where secret messages are exchanged. In Aba, after every vowel we add "b" and add that same vowel. Write a method that takes in a sentence string and returns a new sentence representing its Aba translation. Capitalized words of the original sentence should be properly capitalized in the new sentence.
function abaTranslate(sentence) {
var words = sentence.split(" ");
const vowels = 'AEIOUaeiou';
var newStr = "";
var char = words[i];
for (var i = 0; i < sentence.length; i++) {
if (words.includes(vowels)) {
newStr += (words + "b")
}
}
return newStr;
}
console.log(abaTranslate("Cats and dogs")); // returns "Cabats aband dobogs"
You don't need to split your sentence into individual words, as you're not interested in looking at the words, but rather the individual characters in the sentence. With this in mind, you can use the current loop that you have, and for each i grab the current character from the input sentence at index i.
If the current character is a vowel (ie: if it is included in the vowels string), then you know the current character is a vowel, and so, you can add the current character separated by a "b" to your output string. Otherwise, it if its not a vowel, you can just add the current character to the output string.
See example below:
function abaTranslate(sentence) {
const vowels = 'AEIOUaeiou';
var newStr = "";
for (var i = 0; i < sentence.length; i++) {
var currentCharacter = sentence[i];
if (vowels.includes(currentCharacter)) { // the current character is a vowel
newStr += currentCharacter + "b" + currentCharacter;
} else {
newStr += currentCharacter; // just add the character if it is not a vowel
}
}
return newStr;
}
console.log(abaTranslate("Cats and dogs")); // returns "Cabats aband dobogs"
If you want to use JS methods to help you achieve this, you could use .replace() with a regular expression. Although, it's probably better to try and understand the above code before diving into regular expressions:
const abaTranslate = sentence => sentence.replace(/[aeiou]/ig, "$&b$&");
console.log(abaTranslate("Cats and dogs")); // returns "Cabats aband dobogs"

Without using the reverse() method. How do I maintain the original string order, space and punctuation on string that was reverse?

I am able to use a for loop without using a helper method to reverse the string. But, how do I maintain the original order, space, and punctuation on the string?
Without using the reverse() helper method I am able to reverse the string but I cannot maintain the order of the words and punctuations.
// Reverse preserving the order, punctuation without using a helper
function reverseWordsPreserveOrder(words) {
let reverse = '';
for (let i = words.length -1; i >= 0; i--) {
reverse += words[i];
}
return reverse;
}
console.log(reverseWordsPreserveOrder('Javascript, can be challenging.'))
// output-> .gnignellahc eb nac ,tpircsavaJ
I expect the result to be like this:
// output-> tpircsavaJ, nac eb gnignellahc.
I'd use a regular expression and a replacer function instead: match consecutive word characters with \w+, and in the replacer function, use your for loop to reverse the substring, and return it:
function reverseSingleWord(word) {
let reverse = '';
for (let i = word.length -1; i >= 0; i--) {
reverse += word[i];
}
return reverse;
}
const reverseWordsPreserveOrder = str => str.replace(/\w+/g, reverseSingleWord);
console.log(reverseWordsPreserveOrder('Javascript, can be challenging.'))
If you are trying to do it manually — no reverse() of regexs, you could:
• Defined what you mean by punctuation. This can just be a set, or using an ascii range for letters, etc. But somehow you need to be able to tell letters from non letters.
• Maintain a cache of the current word because you are not reversing the whole sentence, just the words so you need to treat them individually.
With that you can loop through once with something like:
function reverseWordsPreserveOrder(s){
// some way to know what is letter and what is punt
let punct = new Set([',',' ', '.', '?'])
// current word reversed
let word = ''
// sentence so far
let sent = ''
for (let l of s){
if (punct.has(l)) {
sent += word + l
word = ''
} else {
word = l + word
}
}
sent += word
return sent
}
console.log(reverseWordsPreserveOrder('Javascript, can be challenging.'))
Having said this, it's probably more efficient to use a regex.
If you are only averse to reverse because you think it can't do the job, here is a more semantic version (based on #CertainPerformance's), in ES6 you can use the spread syntax (...) with the word string (as strings are iterable):
function reverseSingleWord(word) {
return [...word].reverse().join('');
}
const reverseWordsPreserveOrder = str => str.replace(/\w+/g, reverseSingleWord);
console.log(reverseWordsPreserveOrder('Javascript, can be challenging.'))

Remove all consonants in a string before a vowel then add a character

I want to remove all consonants in a string before the occurrence of a vowel and then replace it with an 'r'.
This means that 'scooby' will become 'rooby', 'xylographer' will become 'rographer' and so on. This is the algorithm I came up with:
1. Check if input type is not a string.
2. Use a variable(newStr) to hold lowercase conversion and splitted word.
3. Declare a variable(arrWord) to hold the length of the array.
4. Another variable called regex to check if a string starts with a consonant
5. Variable newArr holds the final result.
6. Search through the array, if the string does not start with a consonant
join it and return it.
7. Else keep looking for where the first vowel occurs in the word.
8. When found, remove all characters(consonants) before the vowel occurence
and replace them with an r.
9. Join the array together.
I have been able to come up with this:
const scoobyDoo = str => {
if(typeof str !== 'string'){
return 'This function accepts strings only';
}
let newStr = str.toLowerCase().split('');
let arrWord = newStr.length;
let regex = /[aeiou]/gi;
for (let i = 0; i < arrWord; i++){
if (newStr[0].match(regex)) {
let nothing = newStr.join('');
return nothing;
}
else {
let vowelIndex = newStr.indexOf(str.match(regex)[0]);
newStr.splice(0, vowelIndex, 'r');
return newStr.join('');
}
}
}
console.log(scoobyDoo('scOoby'));
I tested out the program again by capitalizing the first vowel index and instead of 'rooby' I get 'rscooby'. Why is that so?
Can you once try with following code in your else and see the changes
else {
var vowelIndex = newStr.indexOf(str.match(regex)[0]);
newStr.splice(0, vowelIndex, 'r');
return newStr.join("");
}
Is it not much easier like this? Or am I missing something??
'xylographer'.replace(/^\s*[^aieou]+(?=\w)/i,function(m,o,s){return "r"})
//"rographer"
'scooby'.replace(/^\s*[^aieou]+(?=\w)/i,function(m,o,s){return "r"})
//"rooby"
you could just use one reg expression for the whole algorithm and no need to split your string no more.
regexp to use.
/^[^aouie, AOUIE]+(?=[aouie, AOUIE])/g
of course you can readjust regexp to suit you more but this will get your main requirement.
On the line immediately after the else statement, I just called .toLowerCase() on it and it was fixed:
let vowelIndex = newStr.indexOf(str.match(regex)[0].toLowerCase());
I like keeping my code simple and readable
function pattern(str){
var vowelIndex = str.indexOf(str.match(/[aeiou]/)); //returns index of first vowel in str
return "r" + str.slice(vowelIndex);
}
console.log(pattern('xylographer'));

Categories

Resources