I've coded myself into a hole and though it would be easier to start again, there is still a lesson to be learned here (it's just practice anyway).
I'm building a caesar cipher which will accept two parameters: the message, and the cipher key. Each letter is compared to its corresponding letter in the cipher key, then changed to a new character code.
I'm having a hell of a time figuring out how to turn an array of character codes into an array (or better yet, a string) of characters.
Here's my code:
function cipher(message, cipherKey) {
//convert the message and cipher key to arrays of their character codes
var messageArr = message.toLowerCase().split("").map(x => x.charCodeAt() - 97);
var cipherKeyArr = cipherKey.toLowerCase().split("").map(x => x.charCodeAt() - 97);
//create new array for the ciphered array, which will turn back to a string
var cipheredArr = [];
//loop through both the cipher key value and message key value to
//create the ciphered array
for (var i = 0; i < messageArr.length; i++) {
cipheredArr[i] = messageArr[i] + cipherKeyArr[i];
if (cipheredArr[i] >= 26) {}
}
//go through the ciphered array and make it loop back through
//the alphabet once it goes past z
for (var i = 0; i < cipheredArr.length; i++) {
if (cipheredArr[i] >= 26) {cipheredArr[i] = cipheredArr[i] - 26;}
}
//display on webpage
return cipheredArr;
}
So the cipheredArr is an array of numbers (character codes) but I can't find a good way to iterate through it and change them back into letters. The .fromCharCode() syntax is confusing me for this purpose.
To get an array of characters for an array of character codes, use map:
var chars = codes.map(code => String.fromCharCode(code));
(Note: Just codes.map(String.fromCharCode) won't work, String.fromCharCode would make inappropriate use of the second and third arguments map passes the callback.)
To get a string from those codes:
// ES2015+
var str = String.fromCharCode(...codes);
// ES5 and earlier:
var str = String.fromCharCode.apply(null, codes);
fromCharCode returns a string made up of the code units you pass it as discrete arguments (MDN | spec).
Related
I was taking on a JS challenge to take a first/last name string input and do the following:
swap the first letter of first/last name
convert all characters to lowercase, except for the first characters, which need to be uppercase
Example:
input: DonAlD tRuMp
output: Tonald Drump
The following is the code I came up with:
const input = prompt("Enter a name:")
function switchFirstLetters(input) {
let stringArray = input.split('');
for(let i=0; i < stringArray.length; i++) {
if(stringArray[i - 1] === ' ') {
[stringArray[0], stringArray[i]] = [stringArray[i], stringArray[0]]; // destructuring
}
}
return result = stringArray.join('');
}
let swappedString = switchFirstLetters(input);
function capFirstLetters(swappedString) {
let stringArray = swappedString.toLowerCase();
stringArray = stringArray.split('');
stringArray[0] = stringArray[0].toUpperCase();
for(let i=0; i < stringArray.length; i++) {
if(stringArray[i - 1] === ' ') {
stringArray[i] = stringArray[i].toUpperCase();
}
}
return result = stringArray.join('');
}
let finalString = capFirstLetters(swappedString);
console.log(finalString);
My thought process for the switchFirstLetters function was:
Create an array from the string parameter
Run through the array length. If the value of the element prior the current element is equal to ' ', use destructuring to swap the current element with the element at index 0
Concatenate elements into a new string and return that value
My thought process for the capFirstLetters function:
Convert all characters in the string to lowercase (this could be handled outside of the function as well)
Create an array from the new, lowercase string
Make character at index 0 be uppercase (this could also be integrated into the for loop)
Run through the array length. If the value of the element prior to the current element is equal to ' ', convert that element to uppercase.
Concatenate array elements into a new string
The code works, but I'm still early in my coding journey and realize it's likely not an ideal solution, so I was wondering if anyone here could help me optimize this further to help me learn. Thanks!
You could also use a regular expression to replace the first letters:
let name = "DonAlD tRuMp";
let result = name.toLowerCase().replace(/(\S)(\S*\s+)(\S)/g, (_, a, b, c) =>
c.toUpperCase() + b + a.toUpperCase()
);
console.log(result);
The regular expression uses \S (a non-white-space character), \S* (zero or more of those), \s+ (one or more white-space characters) and parentheses to create capture groups. These three groups map to a,b,c parameters in the callback function that is passed to replace as second argument. With these parts the replacement string can be constructed. Both the capitalisation and the switch happen in the construction.
If the replace function is a little overwhelming, my attempt introduces the for-of loop, the substring string method, array slice as well as the && short circuit evaluation. You should also be aware you can access a given character of a string using the square bracket syntax, just like array, but string has it's own set of methods which tend to have different names.
Definitely take a look at the replace function, to make your v2.
const rawNameInput = "DonAlD jUnior tRuMp"
const nameInput = rawNameInput.trim()
const rawNameWords = nameInput.split(" ")
const nameWords = []
for (const word of rawNameWords) {
const first = word[0].toUpperCase()
const rest = word.substring(1).toLowerCase()
nameWords.push(first + rest)
}
const middleNames = nameWords.slice(1, -1).join(" ")
const lastIdx = nameWords.length - 1
const newFirstName = nameWords[lastIdx][0] + nameWords[0].substring(1)
const newLastName = nameWords[0][0] + nameWords[lastIdx].substring(1)
console.log(`${newFirstName} ${middleNames && middleNames + " "}${newLastName}`)
I'm trying to solve a problem: Given a string made of digits [0-9], return a string where each digit is repeated a number of times equals to its value. I did the repetition of numbers, how next - I don’t know.
function explode(s) {
for (let i=0; i < s.length; i++){
let y = s[i].repeat(s[i]);
console.log(y);
}
}
You were only missing a result string to collect the parts
function explode(input) {
let result = '';
let s = input.toString(); // So that input can also be a number
for (let i=0; i < s.length; i++){
let y = s[i].repeat(s[i]);
result += y;
}
return result;
}
Javascript is good at coercing numbers to string and vice versa but I prefer to make clear when a numerical character is being treated as a number and when it is intended to be a string.
My snippet tests processing of numbers, string representations of numbers, strings having mixtures of number and letter characters, and letter strings.
it makes use of array.split() to for the character array, array.map() to process the characters (including parseInt to formally change the character to a number when used as the argument the string.repeat(), and array.join() to return the desired numeric string after processing:
let number = 6789;
let numberString = "12345";
let badNum = "3bad2Number";
let noNum = "justLetters";
console.log(expandNums(number));
console.log(expandNums(numberString));
console.log(expandNums(badNum));
console.log(expandNums(noNum));
function expandNums(numOrString) {
let numString = numOrString.toString();
let chars = numString.split('');
let processed = chars.map(char => char.repeat(parseInt(char)));
return processed.join('');
} // end function expandNums
The function performs well under all use situations tested, so is unlikely to throw an error if a bad argument is passes. It also does a good job with the mixed letter/number example.
I'm writing a JavaScript function that has to take in a string argument & determine the word or words with the maximum number or repeated (or most frequent) non sequential characters and return that word or words.
The way that I went about solving this problem was to first find the maximum number of times a character was repeated per word and record that number to use later in a function to test against every word in the string (or the array of strings as I later split it); if the word met the conditions, it's pushed into an array that I return.
My maxCount function seemed to work fine on its own but when I try to make it work together with my other function to get the words with max repeated chars returned, it's not working in JS Fiddle - it keeps telling me that "string.split is not a function" - I'll admit that the way I'm using it (string.split(string[i]).length) to analyze words in the string letter by letter is a bit unconventional - I hope there's some way to salvage some of my logic to make this work in the functions that can work together to get the results that I want.
Also, I don't know if I'm using Math.max correctly/in a "legal" way, I hope so. I've tried switching my variable name to "string" thinking that would make a difference but it did not even though my arguments are of the string variety and it's a string that's being represented.
Here's a link to my Fiddle:
https://jsfiddle.net/Tamara6666/rdwxqoh6/
Here's my code:
var maxCount = function (word) {
/// var maxRepeats = 0;
var numArray = [];
var string = word;
for (var i = 0, len = string.length; i < len; i++) {
//split the word('string') into letters at the index of i
numArray.push((string.split(string[i]).length) -1);
}
var max = Math.max(...numArray);
return max;
}
///console.log(maxCount("xxxxxxxxxxxxx"));
var LetterCount = function(string){
var repeatedChars = 0;
var wordArray=[];
var stringArray = string.split(" ");
for (var i = 0; i < stringArray.length; i++){
var eachWord = stringArray[i];
var maxRepeats = maxCount(stringArray);
if (repeatedChars < maxRepeats) {
repeatedChars = maxRepeats;
wordArray = [eachWord];
}else if (repeatedChars == maxRepeats) {
wordArray.push(eachWord);
}
}
return wordArray;
};
console.log(LetterCount("I attribute my success to cats"));
//should return ["attribute", "success"]
*** I've tried to map this first function onto the array formed when I split my string at the spaces but it is just returned me an empty array (I also might not have been using map correctly in this example); I also have tried using valueOf to extract the primitive value out of the array from the first function which also didn't work. I'm not really sure what to do at this point or what angle to take- I feel if I understood more what was going wrong I could more easily go about fixing it. Any help would be much appreciated. Thanks!
You are passing an array to maxCount at line 20, while it expects a string:
var maxRepeats = maxCount(stringArray);
You should use:
var maxRepeats = maxCount(eachWord);
If you are getting split is not a function error then first make sure that your string isn't null by printing it on console. If it isn't null then confirm that its a string not an array or some other thing.
I have the below code which works. But would using RegExp be a more efficient code?
(Return true if the string in the first element of the array contains the letters of the string in the second element of the array.)
function mutation(arr) {
var first = arr[0].split("");
var second = arr[1].split("");
var answer = false;
for (var e = 0; e < second.length; e++) {
answer = false;
for (var i = 0; i < first.length; i++) {
if (first[i] === second[e]) {
answer = true;
}
}
}
return answer;
}
mutation(['hello', 'hey']);
Using regex to do this check as the code you've shown above:
var a = 'hello how are you';
var b ='hey you there';
var result = new RegExp(b.split(/ +/)[0].split('').join('|')).test( a.split(/ +/)[0] );
//=> true
First split(/ +/)[0] is used to get first word from both strings
Then split('') is used to get each letter from first word of 2nd input
join('|') is used to join them by | to get regex as h|e|y
new RegExp is used for construct a regex object
test is used to execute the regex
In ES6 it can be written as follows:
function mutation(lettersSource, checkedString) {
var referenceLetters = new Set(lettersSource); // get letters from string
var usedLetters = new Set(checkedString);
// create union of letters in both strings
var lettersUnion = new Set([...usedLetters, ...referenceLetters]);
return lettersUnion.size === referenceLetters.size;
}
This solution is O(m+n+k) where m and n is number of letters in strings, and k is number of used letters.
Your solution is O(m*n), quite inefficient.
In ES5 Object can be used as well as dictionary to collect only letters, but it will be slower for many possible letters (Unicode strings).
If you don't have to care about memory usage, you can create dense array using .charCodeAt to index elements in array for O(1) access to checking if elements exists (O(m+n+k)).
I want to send the string to an encryption function which accepts an array of four (32-bit) integers.
So how to convert string to array of 32 bit integers in javascript and divide it to send it to function?
This smells of homework, but here you go.
Method 1:
Assuming you want to convert four characters in a string to ints, this will work:
// Declare your values.
var myString = "1234";
// Convert your string array to an int array.
var numberArray[myString.length];
for (var i = 0; i < myString.length]; i++)
{
numberArray[i] = int.parseInt(myString[i]);
}
// Call your function.
MyEncryptionFunction(numberArray);
Method 2:
Assuming you want to convert four characters to the numeric values of their chars, this will work:
// Declare your values.
var myString = "1,2,3,4";
// Convert your string array to an int array.
var numberArray[myString.length];
for (var i = 0; i < myString.length]; i++)
{
numberArray[i] = myString.charCodeAt(i);
}
// Call your function.
MyEncryptionFunction(numberArray);
Method 3:
Assuming you want to split a group of four numbers separated by a consistent delimiter, this will work.
// Declare your values.
var splitter = ",";
var myString = "1,2,3,4";
// Convert myString to a string array.
var stringArray[] = myString.split(splitter);
// Convert your string array to an int array.
var numberArray[stringArray.length];
for (var i = 0; i < stringArray.length]; i++)
{
numberArray[i] = int.parseInt(stringArray[i]);
}
// Call your function.
MyEncryptionFunction(numberArray);
Use string.charCodeAt(i), to get the numeric char code of string string at position i. Depending on your used encryption, you can apply an own compression method, to combine multiple char codes (most char codes are far smaller than 32 bits).
Example of separating a string in an array consisting of pairs (4 chars):
var string = "A sstring dum doo foo bar";
var result = [];
string += Array((5-(string.length%4))%5).join(" "); //Adding padding at the end
for(var i=3, len=string.length; i<len; i+=4){
result.push([string.charCodeAt(i-3), string.charCodeAt(i-2),
string.charCodeAt(i-1), string.charCodeAt(i)]);
}