JS Caesar Cipher - Need help reviewing my code for repeating letters - javascript

I created a function to encode or decode messages below. I am struggling with finding the error in my code however. I have the function caesar(str, num) that is moving the letters of the alphabet (in str) over one place by (num). For example when I input caesar('Aaa', 1) I expect 'Bbb' in return, but with my function I am getting 'BBbb'. And for example when I input caesar('AAAAaaaa',1) I get 'BBBBBBBBbbbb'. Not sure why the upper cases are repeating and printing twice but the lower cases are fine. Thanks for any help.
const caesar = function(str, num) {
let secret = '';
for ( let i = 0; i < str.length; i++) {
let index = str.charCodeAt(i);
if (index >= 65 && index <= 90) {
secret += String.fromCharCode(index + num);
} else (index >= 97 && index <= 122)
secret += String.fromCharCode(index + num);
}
return secret;
}
console.log(caesar('Aaa', 1));

Related

Why doesn't the second code work the same as the first code?

The First Code works correctly and displays the right answer which is 19.
function findLongestWordLength(str) {
let high = 0;
let word = str.split(" ");
for(let i = 0; i < word.length; i++)
{
if (word[i].length > high)
high = word[i].length;
}
console.log(high)
}
findLongestWordLength("What if we try a super-long word such as otorhinolaryngology");
The second code below, only works until the word "super-long" and logs 10 as the final output, which is incorrect. I'm trying to figure out what goes wrong here. It works fine with other sentences,
FOR REFERENCE
The str.length in the sentence is 60(spaces included), and there are 9 spaces in between the entire sentence
function findLongestWordLength(str) {
let high = 0;
let count = 0;
for (let i = 0; i < str.length; i++) {
if (str[i] != " ") {
count++;
} else {
if (count >= high)
high = count;
count = 0;
}
}
return high;
}
findLongestWordLength("What if we try a super-long word such as otorhinolaryngology");
count could be holding a non-zero value at the end of the loop, which will get ignored. Just add a check at the end of the loop as well to update that value.
for(let i = 0; i < str.length; i++)
{
if (str[i] != " ") count++;
else
{
if(count >= high) high = count;
count = 0;
}
}
if(count >= high) high = count;
In order to get 19, you have to end your string with a space. Because the last character of "otorhinolaryngology" is a 'y', you simply increment count at the very end and you never hit your else condition which will update high. Put a space at the end, add an extra condition checking if i == str.length -1 Or simply use the length algorithm, or, as I would prefer, use a reducer like so:
const findLongestWordLength = str => str.split(' ').reduce((acc, word) => word.length > acc ? word.length : acc, 0)

Caesar Cipher technique and reverse case in javascript

I am beginner and want to make my own function.
I want to hash the password by shifting every character by given x
positions and reverse to lowercase/uppercase.
I think the code below should return "EFGH7654" but it return 55 with no error message.
How can I fix it? Is it because of I put a function in a function?
Or I type wrong any thing?
function hashPassword(password, x) {
// password is a string, x is a number
// return a string
// (ex. password = 'ab1By', x = 3 so it should return "DE4eB")
function shift(text, s) {
result = "";
for (let i = 0; i < text.length; i++) {
let char = text[i];
if (char.toUpperCase(text[i])) {
let ch = String.fromCharCode((char.charCodeAt(0) + s - 65) % 26 + 65);
result += ch;
} else {
let ch = String.fromCharCode((char.charCodeAt(0) + s - 97) % 26 + 97);
result += ch;
}
}
return result;
}
function reversecase(x) {
var output = '';
for (var i = 0, len = x.length; i < len; i++) {
var character = x[i];
if (character == character.toLowerCase()) {
// The character is lowercase
output = output + character.toUpperCase();
} else {
// The character is uppercase
output = output + character.toLowerCase();
}
}
return output
}
var str = "";
var result = "";
var charcode = "";
for (var i = 0; i < password.length; i++) {
if (typeof password[i] === typeof str) {
char = shift(password[i], x)
charcode = reversecase(char)
result += charcode;
} else {
num = password[i] + x
number = num % 10
result += number.toString()
}
}
return result
};
console.log(hashPassword("abcd4321", 4))
There a quite some problems in your code.
The first problem here is not only the nesting, but the fact that you're defining the result variable in the outer function scope using the var keyword. Then you use (read/write) that variable in different places.
In function shift() (also in return statement)
In the outer function (also in return statement)
The thing you have to understand is, that you're referring to the same variable result every time. To ensure that your variables are scoped, i.e. are only valid within a block (if statement, function body, etc.), you should use the let or const keywords. This makes your code a lot safer.
The second problem are some assumptions you make regarding data types. If you have a string let s = "my string 123", the expression typeof s[x] === 'string' will be true for every x in s.
Another problem is the algorithm itself. The outer function hashPassword() iterates over all characters of the input string. Within that loop you call function shift(password[i], x), passing a single character. The first parameter of shift() is called text - and there is another for loop (which is confusing and does not make sense).
To make things short, please have a look at this simplified version:
function shift(char, x) {
let result;
const code = char.charCodeAt(0);
if (code >= 65 && code < 91) {
result = String.fromCharCode((code + x - 65) % 26 + 65);
}
else if (code >= 48 && code <= 57) {
result = String.fromCharCode((code + x - 48) % 10 + 48);
}
else {
result = String.fromCharCode((code + x - 97) % 26 + 97);
}
return result;
}
function reverseCase(character) {
if (character === character.toLowerCase()) {
return character.toUpperCase();
}
else {
return character.toLowerCase();
}
}
function hashPassword(password, x) {
let result = "";
for (let i = 0; i < password.length; i++) {
const char = shift(password[i], x);
result += reverseCase(char);
}
return result;
}
console.log(hashPassword("abcd4321", 4)); // Output: EFGH8765

Need help solving a javascript Caesar Cipher problem

I'm stuck doing the Caesar cipher problem. For those of you familiar with the problem, I'm not able to wrap around the alphabet, for example if I want to shift the string 'z' by 1, instead of getting 'a', i'll get '['. I know the reason this happens but I'm not able to come up with the proper code. Any help will be appreciated.
Here's my code:
const caesar = function(word, num) {
let solved = ""
num = num % 26;
for (let i = 0; i < word.length ; i++) {
let ascii = word[i].charCodeAt();
if ((ascii >= 65 && ascii <= 90) || (ascii >= 97 && ascii <= 122)) {
solved += String.fromCharCode(ascii + num) ;
} else {
solved += word[i]
}
}
return solved;
}
You need to take the modulus under 26 after subtracting the char code of the first letter of the alphabet and add it back afterward to allow the cipher to wrap around. You will need to handle capital and lowercase letters separately.
const caesar = function(word, num) {
let solved = ""
num = (num%26 + 26) % 26;
for (let i = 0; i < word.length ; i++) {
let ascii = word[i].charCodeAt();
if ((ascii >= 65 && ascii <= 90)) {
solved += String.fromCharCode((ascii - 'A'.charCodeAt(0) + num)%26
+ 'A'.charCodeAt(0)) ;
} else if(ascii >= 97 && ascii <= 122){
solved += String.fromCharCode((ascii-'a'.charCodeAt(0) + num) % 26
+ 'a'.charCodeAt(0));
} else {
solved += word[i]
}
}
return solved;
}
console.log(caesar("abcdefghijklmnopqrstuvwxyzABCDEFGHI", 7));
Do the modulus when you add num to your ascii value. This way you don't scroll past the end of the range (and loop around if you are through the modulus (remainder)).
solved += String.fromCharCode((ascii + num)%26) ;
Out of interest, the ASCII codes for 'A` and 'a' are 0x41 and 0x61 respectively: upper and lower case letters differ by whether bit 0x20 is set (lower case) or not set (upper case).
Hence a bit-bashing algorithm to circular shift ASCII letters and maintain case would be strip the lower case bit, perform the shift, and re-insert the case bit:
"use strict";
function caesarShiftLetter( letter, num) {
let code = letter.charCodeAt(0);
let lowerCaseBit = code & 0x20; // 0 or 0x20
let upperCaseCode = code - lowerCaseBit;
if( upperCaseCode < 0x41 || upperCaseCode >= 0x41 + 26) {
return letter;
}
num = 26 + num%26; // handle large negative shift values
upperCaseCode = ((upperCaseCode - 0x41) + num) % 26 + 0x41;
return String.fromCharCode( upperCaseCode | lowerCaseBit);
}
// to test:
function caesarShiftString( str, num) {
return Array.from(str).map(char => caesarShiftLetter( char, num)).join('');
}
console.log( caesarShiftString(
"abc ... xyz, ABC ... XYZ, I, II, III, IV, V, VI, VII, VIII, IX, X - 1,3,4,5,6,7,9, 10",-22
)
);

Levenshtein distance from index 0

I've been working through "The Algorithm Design Manual" section 8.2.1 Edit Distance by Recursion. In this section Skiena writes, "We can define a recursive algorithm using the observation that the last character in the string must either be matched, substituted, inserted, or deleted." That got me wondering, why the last character? This is true for any character based on the problem definition alone. The actual Levenshtein distance algorithm makes recursive calls from the back of the strings. Why? There's no reason you couldn't do the opposite, right? Is it just a simpler, more elegant syntax?
I'm flipping the algorithm around, so it iterates from the front of the string. My attempt is below. I know my implementation doesn't work completely (ex: minDistance("industry", "interest") returns 5 instead of 6). I've spent a couple hours trying to figure out what I'm doing wrong, but I'm not seeing it. Any help would be much appreciated.
var matchChar = (c,d) => c === d ? 0 : 1;
var minDistance = function(word1, word2) {
var stringCompare = function(s, t, i, j) {
if(i === s.length) return Math.max(t.length-s.length-1,0)
if(j === t.length) return Math.max(s.length-t.length-1,0)
if(cache[i][j] !== undefined) {
return cache[i][j]
}
let match = stringCompare(s,t,i+1,j+1) + matchChar(s[i], t[j]);
let insert = stringCompare(s,t,i,j+1) + 1;
let del = stringCompare(s,t,i+1,j) + 1;
let lowestCost = Math.min(match, insert, del)
cache[i][j] = lowestCost
return lowestCost
};
let s = word1.split('')
s.push(' ')
s = s.join('')
let t = word2.split('')
t.push(' ')
t = t.join('')
var cache = []
for(let i = 0; i < s.length; i++) {
cache.push([])
for(let j = 0; j < t.length; j++) {
cache[i].push(undefined)
}
}
return stringCompare(s, t, 0, 0)
}
The lines
if(i === s.length) return Math.max(t.length-s.length-1,0)
if(j === t.length) return Math.max(s.length-t.length-1,0)
look wrong to me. I think they should be
if(i === s.length) return t.length-j
if(j === t.length) return s.length-i

Count bytes in textarea using javascript

I need to count how long in bytes a textarea is when UTF8 encoded using javascript. Any idea how I would do this?
thanks!
encodeURIComponent(text).replace(/%[A-F\d]{2}/g, 'U').length
Combining various answers, the following method should be fast and accurate, and avoids issues with invalid surrogate pairs that can cause errors in encodeURIComponent():
function getUTF8Length(s) {
var len = 0;
for (var i = 0; i < s.length; i++) {
var code = s.charCodeAt(i);
if (code <= 0x7f) {
len += 1;
} else if (code <= 0x7ff) {
len += 2;
} else if (code >= 0xd800 && code <= 0xdfff) {
// Surrogate pair: These take 4 bytes in UTF-8 and 2 chars in UCS-2
// (Assume next char is the other [valid] half and just skip it)
len += 4; i++;
} else if (code < 0xffff) {
len += 3;
} else {
len += 4;
}
}
return len;
}
[June 2020: The previous answer has been replaced due to it returning incorrect results].
Most modern JS environments (browsers and Node) now support the TextEncoder API, which may be used as follows to count UTF8 bytes:
const textEncoder = new TextEncoder();
textEncoder.encode('⤀⦀⨀').length; // => 9
This is not quite as fast as the getUTF8Length() function mentioned in other answers, below, but should suffice for all but the most demanding use cases. Moreover, it has the benefit of leveraging a standard API that is well-tested, well-maintained, and portable.
If you have non-bmp characters in your string, it's a little more complicated...
Because javascript does UTF-16 encode, and a "character" is a 2-byte-stack (16 bit) all multibyte characters (3 and more bytes) will not work:
<script type="text/javascript">
var nonBmpString = "foo€";
console.log( nonBmpString.length );
// will output 5
</script>
The character "€" has a length of 3 bytes (24bit). Javascript does interpret it as 2 characters, because in JS, a character is a 16 bit block.
So to correctly get the bytesize of a mixed string, we have to code our own function fixedCharCodeAt();
function fixedCharCodeAt(str, idx) {
idx = idx || 0;
var code = str.charCodeAt(idx);
var hi, low;
if (0xD800 <= code && code <= 0xDBFF) { // High surrogate (could change last hex to 0xDB7F to treat high private surrogates as single characters)
hi = code;
low = str.charCodeAt(idx + 1);
if (isNaN(low)) {
throw 'Kein gültiges Schriftzeichen oder Speicherfehler!';
}
return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
}
if (0xDC00 <= code && code <= 0xDFFF) { // Low surrogate
// We return false to allow loops to skip this iteration since should have already handled high surrogate above in the previous iteration
return false;
/*hi = str.charCodeAt(idx-1);
low = code;
return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;*/
}
return code;
}
Now we can count the bytes...
function countUtf8(str) {
var result = 0;
for (var n = 0; n < str.length; n++) {
var charCode = fixedCharCodeAt(str, n);
if (typeof charCode === "number") {
if (charCode < 128) {
result = result + 1;
} else if (charCode < 2048) {
result = result + 2;
} else if (charCode < 65536) {
result = result + 3;
} else if (charCode < 2097152) {
result = result + 4;
} else if (charCode < 67108864) {
result = result + 5;
} else {
result = result + 6;
}
}
}
return result;
}
By the way...
You should not use the encodeURI-method, because, it's a native browser function ;)
More stuff:
Code on GitHub
More on Mozilla Developer Networks
Cheers
frankneff.ch / #frank_neff
Add Byte length counting function to the string
String.prototype.Blength = function() {
var arr = this.match(/[^\x00-\xff]/ig);
return arr == null ? this.length : this.length + arr.length;
}
then you can use .Blength() to get the size
I have been asking myself the same thing. This is the best answer I have stumble upon:
http://www.inter-locale.com/demos/countBytes.html
Here is the code snippet:
<script type="text/javascript">
function checkLength() {
var countMe = document.getElementById("someText").value
var escapedStr = encodeURI(countMe)
if (escapedStr.indexOf("%") != -1) {
var count = escapedStr.split("%").length - 1
if (count == 0) count++ //perverse case; can't happen with real UTF-8
var tmp = escapedStr.length - (count * 3)
count = count + tmp
} else {
count = escapedStr.length
}
alert(escapedStr + ": size is " + count)
}
but the link contains a live example of it to play with. "encodeURI(STRING)" is the building block here, but also look at encodeURIComponent(STRING) (as already point out on the previous answer) to see which one fits your needs.
Regards
encodeURI(text).split(/%..|./).length - 1
How about simple:
unescape(encodeURIComponent(utf8text)).length
The trick is that encodeURIComponent seems to work on characters while unescape works on bytes.
Try the following:
function b(c) {
var n=0;
for (i=0;i<c.length;i++) {
p = c.charCodeAt(i);
if (p<128) {
n++;
} else if (p<2048) {
n+=2;
} else {
n+=3;
}
}return n;
}
set meta UTF-8 just & it's OK!
<meta charset="UTF-8">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
and js:
if($mytext.length > 10){
// its okkk :)
}

Categories

Resources