Format casing of names when first letter is repeated - javascript

I have a function which takes an array of names and returns a new array with each name's first letter transformed to uppercase (and the remaining letters lowercase).
function capMe(arr) {
let newArr = [];
arr.forEach(function(name) {
name = name.replace(name[0], name[0].toUpperCase());
for(let i = 1; i < name.length; i++) {
name = name.replace(name[i], name[i].toLowerCase());
}
newArr.push(name);
});
return newArr;
}
This works for the vast majority of the time. For example, if I call the function like this:
console.log(capMe(['jACKSON', 'pAM', 'wiLliAm']));
I will receive the desired output of > Array ["Jackson", "Pam", "William"].
However, there is one case in which this function does not work. If I call the function with a name which has the first letter repeated, every letter will be lowercase except for the second occurrence of the letter.
For example, If I add gEORGANN to the previous example, I will receive this output:
> Array ["georGann", "Jackson", "Pam", "William"]
How do I solve this issue?

Your current logic is not doing what you think it is. Consider the following line which appears in the loop over each name:
name = name.replace(name[i], name[i].toLowerCase());
This is replacing the first character only which is equal to name[i] with the lowercase version of that letter. Here is how this is throwing off your logic with the input gEORGANN. First, you uppercase the first g, to get GEORGANN. Then, when the loop hits the fourth position, which is a capital G, it replaces the first G with a lowercase g. This leaves us with gEORGANN, but it is not what you intend. The reason for this is that replace just hits the first occurrence.
You might be able to remedy this by doing a replace all instead of a replace. But why not just take the first character in uppercase concatenated with the remainder of the string in lowercase:
arr = ['georGann', 'jACKSON', 'pAM', 'wiLliAm'];
newArr = [];
arr.forEach(function(name) {
newName = name.charAt(0).toUpperCase() + name.substring(1).toLowerCase()
newArr.push(newName);
});
console.log(arr);
console.log(newArr);

Here you go:
const capMe = (arr = []) => {
return arr.map((name) => {
const [first, ...rest] = name;
return `${first.toUpperCase()}${rest.join('').toLowerCase()}`;
});
}
console.log(capMe(['foo', 'foZBaz']));

Your coding logic is fine. The problem is the order of operations. Please see below;
function capMe(arr) {
let newArr = [];
arr.forEach(function(name) {
name = name.toLowerCase();
name = name.replace(name[0], name[0].toUpperCase());
newArr.push(name);
});
return newArr;
}
console.log(capMe(['gEORGANN', 'pAM', 'wiLliAm', 'AA']));

Related

Get number of words in an array starting with certain characters

I'm trying to solve a problem on a testing website and I'm getting confused on trying to find a way to match the letters of a passed in array wordlist.
In the below example it should be returning 2, as ye should be matched twice - in yesterday and yellow.
Without using a RegEx what would be the best way be to go about getting a passed in wordlist to return a number amount based on the 2nd argument input?
This is the code I have so far, that isn't working:
let wordList = ['yesterday', 'develop', 'environment', 'yellow', 'yikes', 'envious']
const countingLetters = (words, inputStart) => {
let total = 0;
for (let i = 0; i < words.length; i++) {
if (inputStart == words[i]) {
total += 1;
}
}
return total;
};
console.log(countingLetters(wordList, 'ye'));
To do what you require without a regex, you can use the filter() function, to search the array by a given condition, and the indexOf() method to ensure that the match is at the start of the string:
let wordList = ['yesterday', 'develop', 'environment', 'yellow', 'yikes', 'envious']
const countingLetters = (words, inputStart) =>
words.filter(word => word.indexOf(inputStart) === 0).length;
console.log(countingLetters(wordList, 'ye'));

Iterate over two separate arrays of different length and populate a third using a condition in JavaScript?

I've done something similar in the past with a nested loop, but for some reason I can't get my mind around this to get it to work. I keep running into problems that involve indexing of two separate arrays and it's been a continual stumbling block
In this case, I'm trying to sort a string. The string includes letters and numbers, the task is to sort the letters in reverse alphabetical order while keeping the numbers at their same index.
I've come up with this solution (probably not the most efficient), but can't get the sortString array to come together so that I can join the letters and numbers back into a string.
function reverse(str) {
// split the str into an array
const arr = [...str]
// converts each element in arr to a number, letters are string 'NaN'
const numArray = arr.map(x=> Number(x)).map(x=> x >= 0 ? x : String(x))
// array of the sorted letters
const letters = arr.filter(x=> !/[0-9]/g.test(x)).reverse()
// initiate empty array to hold the combined numbers and letters
let sortString = []
// Use for loop to cycle through and replace elements that are 'NaN' with letters from the letter array. All pushed to sortString.
for (let i=0; i<arr.length; i++) {
sortString.push(numArray[i] === 'NaN' ? letters[0] : numArray[i])
}
return sortString
}
reverse("ab89c") // output should be "cb89a"
You could get an array of non digits, sort it and map the splitted string with the sorted letters in places if not a digit.
const
reverse = string => {
const
array = Array.from(string),
letters = array
.filter(v => /\D/.test(v))
.sort();
return array
.map(v => /\D/.test(v) ? letters.pop() : v)
.join('');
};
console.log(reverse("ab89c"));
A slightly different approach takes a Proxy for the wanted items of sorting:
How to sort only part of array? between given indexes
Here's code that works:
Explanation of how the code works is in-line as comments.
Basically it takes the numbers out, sorts the letters in reverse, and puts the sorted letters back in the right place.
Because it's step-by-step, you could add console log on each variable after it's assigned to see how it works step by step.
function reverse(input) {
// This function from: https://stackoverflow.com/a/32567789/569302
function testIsLetter(c) {
return c.toLowerCase() != c.toUpperCase();
}
// Convert from array to string to process character by character
let inputAsArray = input.split('');
// This is where we'll lookup where to put the letters when we're done
let mapped = inputAsArray.map((s) => {
return {
s,
isLetter: testIsLetter(s)
}
})
// Now that we've captured where the letters are, take the numbers (non-letters) out
let filtered = mapped.filter(m => m.isLetter)
// Convert the object into just letters so they're easily compared when we sort
let filteredAsLettersArray = filtered.map(f => f.s)
// Sort ascending
filteredAsLettersArray.sort()
// Reverse to sort descending
filteredAsLettersArray.reverse()
// Now we need to put the letters back.
let resultAsArray = [];
let letterIndex = 0;
mapped.forEach(m => {
// If it's a letter, we use the sorted result (incrementing letterIndex each time)
if (m.isLetter) {
resultAsArray.push(filteredAsLettersArray[letterIndex]);
letterIndex++;
} else {
// Otherwise we use the number
resultAsArray.push(m.s);
}
});
let result = resultAsArray.join('');
return result;
}
console.log(reverse("ab89c"));
console.log(reverse("1a2eb8f9c"));

Using values from dictionary to code a string

I am trying to write a function -encode(text, keyText)- that takes a string, generally a word and encodes it by changing every letter of the text with that letter's place in the keyText.
If a letter occurs multiple times in the keyText, then it should use every occurrence in encoding process in the same order they occur in the keyText. If it occurs more times in the text, than it loops back to the first, so the first letter is replaced by that letter's place in the keyText, and the second occurrence of that letter is replaced by the second place in the keyText, if the letter comes a third time in the text but only occurred in the keyText twice, than it is replaced by the first place again and so on.
I also wrote a function, getKeys, that takes a string and returns a "dictionary" that gives the places in that sentence for each letter.
Let's say keyText is "And not just the men, but the women and the children, too"
getKeys(keyText) would return : {"A":[1, 29], "N":[2, 4], "D":[3], "O":[5, 25, 44 ,45] ...}
so encode("anna", keyText) should return this:
[1, 2, 4, 29]
function encode(text, keyText) {
text = text.toUpperCase();
var key = getKeys(keyText);
var list = [];
var counter = 1;
for(let char of text){
var len = key[char].length;
if(counter > len){
counter = 0;
}
list.push(key[char][counter])
counter++;
}
}return list;
The obvious problem with my code is that counter is incremented for all the letters, and not just when it needs to get the second or third value. But if i put the counter in the for loop, than it's not going to work either. I couldn't find a way to tie counter to each character of Text so that it is only incremented if that character has come up more than once.
Once getKeys is called, and you have an object, you need separate counters for each property in the object. You could use the keys object to construct another one with the same keys, but whose values are indicies indicating the current counter for that key. On each character iteration, look up the associated counter for the character, save the value to return, and increment the counter:
function encode(text, keyText) {
// var key = getKeys(keyText);
const keysObj = {"A":[1, 29], "N":[2, 4], "D":[3], "O":[5, 25, 44 ,45] };
const counters = Object.fromEntries(
Object.keys(keysObj).map(key => [key, 0])
);
return [...text.toUpperCase()]
.map((char) => {
const thisCharNumArr = keysObj[char];
const num = thisCharNumArr[counters[char]];
counters[char] = (counters[char] + 1) % thisCharNumArr.length;
return num;
})
}
console.log(encode('anna'));
console.log(encode('annannnnn'));

Create a function which takes in a word and spells it out, by consecutively adding letters until the full word is completed

Create a function which takes in a word and spells it out, by consecutively adding letters until the full word is completed.
This is my code for the solution. I am super new to JS. Could someone tell me the problem? Thank you.
function spelling(str) {
var str1 = [...str]
var n
str1.index[n] = str.slice(0, n+1)
return str1
}
Expected output:
Test.assertSimilar(spelling("bee"), ['b', 'be', 'bee'])
Test.assertSimilar(spelling("cake"), ['c', 'ca', 'cak', 'cake'
Actual output:
TypeError: Cannot set property 'undefined' of undefined at spelling at
Object.handleError at ContextifyScript.Script.runInThisContext at
Object.exports.runInThisContext
First of all, you never initialized n.
In addition, what is str1.index? It does not exist. If you want to access a cell in an array you should use: array[i].
Moreover, after you defined n, you need to update it for every char.
What I did was to fix all of what I mentioned above, and I created an empty array, and for each char of str I'm pushing to the array a slice of str and update n (++n means to use n after increment it by 1).
function spelling(str) {
var str1 = [];
let n = 0;
for (c in str)
str1.push(str.slice(0, ++n));
return str1;
}
console.log(spelling('bee'));
function spelling(str) {
return str.split('').map((el,i)=>str.slice(0,i+1));
}
Map can take a second parameter which is the index of the array that map is being called on.
You can use substring to achieve this.
The substring() method extracts the characters from a string, between
two specified indices, and returns the new sub string.
function spelling(str) {
let result = [];
for (let i = 1; i <= str.length; i++) {
result.push(str.substring(0, i));
}
return result;
}
console.log(spelling('bee'));
/**
* spelling("hello") // Returns ["h", "he", "hel", "hell", "hello"]
*/
function spelling(str) {
var arr = [];
var len = str.length;
while (len) {
arr.unshift(str.substring(0, len--));
}
return arr;
}

How to split a string by dash at specific position JavaScript

I have a dynamically created array as below:
["171281-0-001-1", "171281-0-001-2"]
Basically, I am creating the value based on a very complex function. But that function does not matter here. Basically for example 171281-0-001-1 is made of Account number + id. 171281-0-001 is Account number and 1 is the id.
The number of dashes can be any number but after the last dash it's always id.
How can I differentiate these from my value and assign it into different variables?
Tried:
for (let key in this.your_reference) {
extractedAccountNumber = key.split('-')[0];
extractedId = key.split('-')[1];
}
But since the dashes can be in any number now, the variables have wrong values. Any idea guys? Thanks in advance
You can use map() and split() to take the item from the last array index:
var arr = ["171281-0-001-1", "171281-0-001-2"];
var idList = arr.map(d => {
d = d.split('-');
return d[d.length - 1];
});
console.log(idList);
You could use a regular expression with two groups: one captured group, which matches anything, then match a dash, then another captured group for digits followed by the end of the string:
["171281-0-001-1", "171281-0-001-2"].forEach((str) => {
// first item in the array is the entire match,
// but you only care about the two capture groups, so ignore the first item:
const [,accountNumber, id] = str.match(/(.+)-(\d+)$/);
console.log(accountNumber);
console.log(id);
});
You can use lastIndexOf and substring for that:
var arr = ["171281-0-001-1", "171281-0-001-2"];
var ids = arr.map(d => d.substring(d.lastIndexOf('-') + 1));
console.log(ids);
const arr = ["171281-0-001-1", "171281-0-001-2"];
const accountDetails = arr.map((item) => {
const itemArr = item.split('-');
const accountNumber = itemArr.slice(0, itemArr.length -1).join('-');
const id = itemArr[itemArr.length - 1];
return {id, accountNumber};
});
console.log(accountDetails);

Categories

Resources