ROT13 cipher in as few lines of code possible - javascript

I have written a function where the values of the letters in a received string are shifted by 13 places.
My solution is highly inefficient and would need completely rewriting if the shift factor was changed.
Here is my solution:
function rot13(str) {
var charArray = str.split("");
var myArray = [];
for (var i = 0; i < charArray.length; i++) {
switch (charArray[i]) {
case "A":
myArray.push("N");
break;
case "B":
myArray.push("O");
break;
case "C":
myArray.push("P");
break;
case "D":
myArray.push("Q");
break;
case "E":
myArray.push("R");
break;
case "F":
myArray.push("S");
break;
case "G":
myArray.push("T");
break;
case "H":
myArray.push("U");
break;
case "I":
myArray.push("V");
break;
case "J":
myArray.push("W");
break;
case "K":
myArray.push("X");
break;
case "L":
myArray.push("Y");
break;
case "M":
myArray.push("Z");
break;
case "N":
myArray.push("A");
break;
case "O":
myArray.push("B");
break;
case "P":
myArray.push("C");
break;
case "Q":
myArray.push("D");
break;
case "R":
myArray.push("E");
break;
case "S":
myArray.push("F");
break;
case "T":
myArray.push("G");
break;
case "U":
myArray.push("H");
break;
case "V":
myArray.push("I");
break;
case "W":
myArray.push("J");
break;
case "X":
myArray.push("K");
break;
case "Y":
myArray.push("L");
break;
case "Z":
myArray.push("M");
break;
case " ":
myArray.push(" ");
break;
case ",":
myArray.push(",");
break;
case "!":
myArray.push("!");
break;
case "?":
myArray.push("?");
break;
case ".":
myArray.push(".");
break;
}
}
console.log(myArray.join(""));
}
rot13("GUR DHVPX OEBJA QBT WHZCRQ BIRE GUR YNML SBK.");
Can you show me a more efficient less cumbersome solution?

Here's one possible implementation, with the ability to pass in any (positive) rotation value and a table of other replacements. Written in ES6.
function rotn(str, rotation = 13, map = {}) {
const table = {}; // New table, to avoid mutating the parameter passed in
// Establish mappings for the characters passed in initially
for (var key in map) {
table[map[key]] = key;
table[key] = map[key];
}
// Then build the rotation map.
// 65 and 97 are the character codes for A and a, respectively.
for (var i = 0; i < 26; i++) {
table[String.fromCharCode(65 + i)] = String.fromCharCode(65 + (i + rotation) % 26);
table[String.fromCharCode(97 + i)] = String.fromCharCode(97 + (i + rotation) % 26);
}
return str.split('').map((c) => table[c] || c).join('');
}
console.log(rotn("Gur dhvpx oebja Qbt whzcrq bire gur ynml Sbk.", 13, {'.': '!'}));

Here is an example using the reduce function:
function rot13(str) {
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
return str.split("").reduce(function(a, b) {
if (chars.indexOf(b) == -1) {
return a + b;
}
return a + chars[(chars.indexOf(b)+13) % chars.length]
}, "");
}
console.log(rot13("GUR DHVPX OEBJA QBT WHZCRQ BIRE GUR YNML SBK."));

function ROT13 (str){
var result = "";
for (var i = 0; i < str.length; i++) {
var charCode = str.charCodeAt(i);
if (charCode >= 65 && charCode <= 90) {
result += String.fromCharCode(((charCode - 65 + 13) % 26) + 65);
} else if (charCode >= 97 && charCode <= 122) {
result += String.fromCharCode(((charCode - 97 + 13) % 26) + 97);
} else {
result += str[i];
}
}
return result;
}

Here's a solution that uses:
replace() with a /[a-z]/ regex so that only alphabetic characters will be modified
indexOf() to convert that alphabetic character to an index
then uses that index to lookup the corresponding cipher character
function rot13(txt) {
return txt.replace(/[a-z]/gi, c =>
"NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"
[ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".indexOf(c) ] );
}
console.log(rot13("GUR DHVPX OEBJA QBT WHZCRQ BIRE GUR YNML SBK."));

Related

JS Split Varables

I have a String like "iyxnhel2jeh" and i want to for each 2 byte I want to split those into a single var.
var string = "iyxnhel2jehe";
var final = "";
while (/*String still has bits*/) {
switch (/*Two byte of string*/) {
case "iy":
final += "x";
break;
case "xn":
final += "o";
break;
case "he":
final += "g";
break;
case "l2":
final += "k";
break;
case "je":
final += "e";
break;
default:
final += "none"
}
}
Whats the best way to cut this string?
You could use regex to split string into 2letter parts, map them onto the characters from switch statement and join array back together, however the optimal way to do that would be to get rid of the switch statement and instead use those sequences of characters as keys of a object.
var string = "iyxnhel2jehe";
var final = string.match(/.{1,2}/g).map(twoletters => {
return {
"iy": "x",
"xn": "o",
"he": "g",
"l2": "k",
"je": "e"
}[twoletters] || "none";
}).join("");
console.log(final)
I'm not sure about the best way, but the following would do what you want...
var string = "iyxnhel2jehe";
var final = "";
for (var i = 0; i < string.length; i+=2) {
switch (string.substr(i,2)) {
case "iy":
final += "x";
break;
case "xn":
final += "o";
break;
case "he":
final += "g";
break;
case "l2":
final += "k";
break;
case "je":
final += "e";
break;
default:
final += "none"
}
}
console.log(final);
var string = "iyxnhel2jehe";
var final = "";
var offset = 0;
while (offset < string.length) {
switch (string.slice(offset, offset + 2)) {
case "iy":
final += "x";
break;
case "xn":
final += "o";
break;
case "he":
final += "g";
break;
case "l2":
final += "k";
break;
case "je":
final += "e";
break;
default:
final += "none"
}
offset += 2;
}
console.log(final);
Instead of trying to consume characters and check whether there are still any left, try splitting the string into 2-character chunks first, and loop through the array:
const s = "iyxnhel2jehe";
let final = "";
const t = s.split('');
const segments = t.map((e, i) => i % 2 === 0 ?
e + (t.length - 1 >= i + 1 ? t[i + 1] : '') :
null)
.filter(x => x != null);
segments.forEach(sg => {
console.log(sg);
switch (sg) {
case "iy":
final += "x";
break;
case "xn":
final += "o";
break;
case "he":
final += "g";
break;
case "l2":
final += "k";
break;
case "je":
final += "e";
break;
default:
final += "none"
}
});
console.log(final);

Is there a way to name the same case but when is 2 times clicked?

I'm translating some interactives for a math school from English to Spanish. I'm having problems adapting the code to numbers in Spanish.
So I'm doing cases to change the order and the text (the numbers in Spanish have a different form to write between 20 and 24) in the console log the case "veinte" works to 20 but when you go 22 the case "veinte" count like 2 times "veinte" case. (see the image below)
I don't know how to make a x2 of the same case. Sorry if I don't know how to make a good explanation of my problem, it's kind of confusing.
for(i = 0 ; i < q.length ; i++)
{
if(q[i] != null)
{
if(LowerCase)
q[i] = q[i].toLowerCase();
switch((3 - q.length) + i)
{
case 0:
switch(q[i]){
case 'Cinco':
q[i] = 'Quinientos';
break;
case 'Siete':
q[i] = 'Setecientos';
break;
case 'Nueve':
q[i] = 'Novecientos'
break;
case 'Uno':
q[i] = 'Cien'
break;
default:
q[i] = q[i] + "cientos";
}
vTemp = o.substring(i + 1);
if(parseInt(vTemp) > 0)
//q[i] = q[i] + " and";
q[i] = q[i] + "";
break;
case 1:
switch(q[i]){
case 'veinte':
q[i] = 'veinti';
break;
default:
q[i] = q[i];
}
if((q[2] != null) && (q[2] != "x"))
q[i] = q[i] + " y";
break;
case 2:
if(q[i] == "x")
q[i] = "";
break;
}
LowerCase = true;
}
else
{
q[i] = "";
}
}

Simplifying Long Switch Statements

I need to brush up on my javascript because it is my weakest language, so I thought "Hey lets make a simple 'translating' program to test my skills". Well I was able to make it translate one way so far(I havent worked on untranslating the stuff people input), but anyway the way it does it is by a series of many cases inside a switch. Im wondering if there is anyway I can simplify the code instead of having a million switch cases. Thanks here is my code.
function main() {
var get = prompt("Enter what you would like to encode!","At the current time decoding is still a WIP").toLowerCase();
var ina = [...get];
for(i = 0; i < ina.length; i++) {
switch(ina[i]) {
case "a":
ina[i] = "z";
break;
case "b":
ina[i] = "y";
break;
case "c":
ina[i] = "x";
break;
case "d":
ina[i] = "w";
break;
case "e":
ina[i] = "v";
break;
case "f":
ina[i] = "u";
break;
case "g":
ina[i] = "t";
break;
case "h":
ina[i] = "s";
break;
case "i":
ina[i] = "r";
break;
case "j":
ina[i] = "q";
break;
case "k":
ina[i] = "p";
break;
case "l":
ina[i] = "o";
break;
case "m":
ina[i] = "n";
break;
case "n":
ina[i] = "m";
break;
case "o":
ina[i] = "l";
break;
case "p":
ina[i] = "k";
break;
case "q":
ina[i] = "j";
break;
case "r":
ina[i] = "i";
break;
case "s":
ina[i] = "h";
break;
case "t":
ina[i] = "g";
break;
case "u":
ina[i] = "f";
break;
case "v":
ina[i] = "e";
break;
case "w":
ina[i] = "d";
break;
case "x":
ina[i] = "c";
break;
case "y":
ina[i] = "b";
break;
case "z":
ina[i] = "a";
break;
default:
ina[i] = ina[i]
};
};
var outa = ina.join("");
document.getElementById("output").innerHTML = outa;
};
You could use an object with properties like
{
a: 'z',
b: 'y',
c: 'x',
// ...
z: 'a'
}
Usage with the default value of ina[i].
ina[i] = object[ina[i]] || ina[i];
You could use a couple string variables to map the letters.
function translateLetter(input) {
const untranslated = "abcdefghijklmnopqrstuvwxyz";
const translated = "zyxwvutsrqponmlkjihgfedcba";
var i = untranslated.indexOf(input);
return translated[i];
}
The switch you're using has logic that can be implemented directly without needing the switch at all via simple math (I believe most modern JS interpreters should JIT away the actual method calls if this is a hot loop, so the cost there should be trivial):
var get = prompt("Enter what you would like to encode!","At the current time decoding is still a WIP").toLowerCase();
var ina = [...get];
for(i = 0; i < get.length; i++) {
var code = get.charCodeAt(i);
if (97 <= code && code <= 122) { // 'a' and 'z' ordinal values
// Invert lowercase letters with simple math and convert back to character
ina[i] = String.fromCharCode((122 + 97) - code);
}
// No need to handle non-lowercase/non-ASCII since ina initialized to match get
}
Just do the math on the ASCII character codes:
function main() {
var get = prompt("Enter what you would like to encode!","At the current time decoding is still a WIP").toLowerCase();
var ina = [...get];
for (i = 0; i < get.length; i++) {
var charNum = get.charCodeAt(i) - 96;
if (charNum > 0 && charNum < 27) {
ina[i] = String.fromCharCode((27 - charNum) + 96);
}
};
var outa = ina.join("");
document.getElementById("output").innerHTML = outa;
};

Javascript case statement in the switch Statement

I have a problem with the 'case' statement in the 'switch' statement in java script.
My question is how to write more than one number in the 'case' statement and save all the work on writing multiple of commands for each number , ill try to explain myself better. i want to write in the case statement the
number 10-14 (10,11,12,13,14).
how can i write it?
thanks for helping and sorry for my bad english.
name = prompt("What's your name?")
switch (name)
{
case "Ori":
document.write("<h1>" + "Hello there Ori" + "<br>")
break;
case "Daniel":
document.write("<h1>" + "Hi, Daniel." + "<br>")
break;
case "Noa":
document.write("<h1>" + "Noa!" + "<br>")
break;
case "Tal":
document.write("<h1>" + "Hey, Tal!" + "<br>")
break;
default:
document.write("<h1>" + name + "<br>")
}
age = prompt ("What's your age?")
switch (age)
{
case "10":
document.write("you are too little" + name)
break;
case "14":
document.write("So , you are in junior high school" + name)
break;
case "18":
document.write("You are a grown man" + name)
break;
default:
document.write("That's cool" + name)
break;
}
Check out this answer Switch on ranges of integers in JavaScript
In summary you can do this
var x = this.dealer;
switch (true) {
case (x < 5):
alert("less than five");
break;
case (x > 4 && x < 9):
alert("between 5 and 8");
break;
case (x > 8 && x < 12):
alert("between 9 and 11");
break;
default:
alert("none");
break;
}
but that sort of defeats the purpose of a switch statement, because you could just chain if-else statments. Or you can do this:
switch(this.dealer) {
case 1:
case 2:
case 3:
case 4:
// Do something.
break;
case 5:
case 6:
case 7:
case 8:
// Do something.
break;
default:
break;
}
use this, if you dont provide break then control will fall down, In this way you can match for group of numbers in switch.
case 10:
case 11:
case 12:
case 14:
case 15: document.write("i am less than or equal to 15");break;
Say you wanted to switch on a number 10-14 (10,11,12,13,14) you can chain the cases together:
switch(number) {
case 10:
case 11:
case 12:
case 13:
case 14:
alert("I'm between 10 and 14");
break;
default:
alert("I'm not between 10 and 14");
break;
}
You can simply omit the break; statement.
switch (2) {
case 1: case 2: case 3:
console.log('1 or 2 or 3');
break;
default:
console.log('others');
break;
}
I wanted to play with the concept a bit, I do not recommend this approach, however you could also rely on a function that will create a control flow function for you. This simply allows some syntaxic sugar.
var createCaseFlow = (function () {
var rangeRx = /^(\d)-(\d)$/;
function buildCases(item) {
var match = item.match(rangeRx),
n1, n2, cases;
if (match) {
n1 = parseInt(match[1], 10);
n2 = parseInt(match[2], 10);
cases = [];
for (; n1 <= n2; n1++) {
cases.push("case '" + n1 + "':");
}
return cases.join('');
}
return "case '" + item + "':";
}
return function (cases, defaultFn) {
var fnStrings = ['switch (value.toString()) { '],
k;
for (k in cases) {
if (cases.hasOwnProperty(k)) {
fnStrings.push(k.split(',').map(buildCases).join('') + "return this[0]['" + k + "'](); break;");
}
}
defaultFn && fnStrings.push('default: return this[1](); break;');
return new Function('value', fnStrings.join('') + '}').bind(arguments);
};
})();
var executeFlow = createCaseFlow({
'2-9': function () {
return '2 to 9';
},
'10,21,24': function () {
return '10,21,24';
}
},
function () {
return 'default case';
}
);
console.log(executeFlow(5)); //2 to 9
console.log(executeFlow(10)); //10,21,24
console.log(executeFlow(13)); //default case
You have already gotten a few answers on how to make this work. However, I want to point out a few more things. First off, personally, I wouldn't use a switch/case statement for this as there are so many similar cases – a classic if/elseif/else chain feels more appropriate here.
Depending on the use-case you could also extract a function and then use your switch/case (with more meaningful names and values, of course):
function getCategory (number) {
if(number > 20) {
return 3;
}
if(number > 15) {
return 2;
}
if(number > 8) {
return 1;
}
return 0;
}
switch( getCategory( someNumber ) ) {
case 0:
// someNumber is less than or equal to 8
break;
case 1:
// someNumber is any of 9, 10, 11, 12, 13, 14, 15
break;
// ...
}
If the intervals are equally spaced, you could also do something like this:
switch( Math.floor( someNumber / 5 ) ) {
case 0:
// someNumber is any one of 0, 1, 2, 3, 4
break;
case 1:
// someNumber is any one of 5, 6, 7, 8, 9
break;
// ...
}
Also, it should be noted that some people consider switch/case statements with fall-throughs (= leaving out the break; statement for come cases) bad practice, though others feel it's perfectly fine.

javascript switch vs loop on array

I have these two functions and I want to know which one is faster. I assume the first one, but what if I have hundreds of cases to evaluate?
function isSpecialKey(k) {
switch (k) {
case 9:
return true;
break;
case 16:
return true;
break;
case 17:
return true;
break;
case 18:
return true;
break;
case 20:
return true;
break;
case 37:
return true;
break;
case 38:
return true;
break;
case 39:
return true;
break;
case 40:
return true;
break;
default:
return false;
break;
}
}
function isSpecialKey(k) {
var arr = [9, 16, 17, 16, 8, 20, 37, 38, 39, 40]
for (i = 0; i < arr.length; i++) { if (k == arr[i]) { return true; } }
return false;
}
It is very unlikely to matter, not even with hundreds of cases. It might start mattering with thousands or tens of thousands but in that case, you probably shouldn't be using JavaScript anyway! At least not in a web browser.
Generally - the second way is the only way that makes sense from a maintenance perspective. I would absolutely take that.
However, for your specific case, there is a better solution.
Instead of doing this, just create a map:
var specialKeys = {
9: true,
16: true,
17: true,
...
40: true
};
Then you can just test it like so:
if(specialKeys[value]) {
...
}
How about?
function isSpecialKey(key) {
return [9, 16, 17, 16, 8, 20, 37, 38, 39, 40].indexOf(key) > -1;
}
Update. I've just remembered there are some browsers that don't support indexOf on arrays, but I forgot which of them, so be careful.
You can use fallthrough in the switch, which makes for a lot less code:
function isSpecialKey(k) {
switch (k) {
case 9:
case 16:
case 17:
case 18:
case 20:
case 37:
case 38:
case 39:
case 40:
return true;
}
return false;
}
Consider also:
function isSpecialKey(k) {
return (
k == 9 ||
k == 16 ||
k == 17 ||
k == 18 ||
k == 20 ||
k == 37 ||
k == 38 ||
k == 39 ||
k == 40
);
}
Use a map; faster yet. "Hundreds" of switches should never come close to passing a code review.

Categories