How to maintain case and ignore spaces in Vigenere Cipher - javascript

I need to be able to keep the same case, i.e. "Attack" will be "Lxfopv", with the key "lemon". In addition, I need to keep any spaces within the message to be encrypted.
I used an if statement to check for whitespace
if(text.charAt(i) == ' '){
continue;
but it doesn't seem to do anything.
function encrypt(text, key) {
var output= '';
var alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
for(var i = 0; i < text.length; i++){
var a= alphabet.indexOf(key.charAt(i % key.length));
var b= alphabet.indexOf(text.charAt(i));
if(text.charAt(i) == ' '){
continue;
}else{
output += alphabet.charAt((a+ b) % alphabet.length);
}
}
return output;
}
if pass in "Attack at Dawn", my desired output should be Lxfopv ef Rnhr but I am recieving LxFopvmHOeIB with the key "lemon".
How can I fix this to get the desired output? Is it something to do with the fact that I have hardcoded my alphabet?

In order to keep the case, you will have to work your transformation on a single case.
Only at the time of adding it to your output, will you convert it to the correct case.
And in order to get the same value than other algorithms which do ignore the space character, you have to use a second iterator variable.
This iterator should get incremented only on valid inputs, and will be used to iterate the key.
inp.oninput = e => log.textContent = encrypt(inp.value, 'lemon');
function encrypt(text, key) {
var output= '';
// single case dictionary
var alphabet = "abcdefghijklmnopqrstuvwxyz";
var low = text.toLowerCase(); // we'll work on this one
for(let i = 0, j = 0; i < text.length; i++){
// here we use `j` for the key
let a = alphabet.indexOf(key.charAt(j % key.length));
let b = alphabet.indexOf(low.charAt(i));
let out = ''; // the character we'll add
if(low.charAt(i) == ' '){
out = ' '; // keep spaces untouched
}else if(b > -1){ // only if valid
out = alphabet.charAt((a+ b) % alphabet.length); // get the ciphered value
j++; // only here we increment `j`
}
if(low[i] !== text[i]) { // if input and lower case are different
// that means that input was upper case
out = out.toUpperCase();
}
output += out;
}
return output;
}
<input id="inp"> <pre id="log"></pre>

Just add the space to your alphabet:
if(text.charAt(i) == ' '){
output += " ";
}

Related

How can I extract all contained characters in a String? [duplicate]

I have a string with repeated letters. I want letters that are repeated more than once to show only once.
Example input: aaabbbccc
Expected output: abc
I've tried to create the code myself, but so far my function has the following problems:
if the letter doesn't repeat, it's not shown (it should be)
if it's repeated once, it's show only once (i.e. aa shows a - correct)
if it's repeated twice, shows all (i.e. aaa shows aaa - should be a)
if it's repeated 3 times, it shows 6 (if aaaa it shows aaaaaa - should be a)
function unique_char(string) {
var unique = '';
var count = 0;
for (var i = 0; i < string.length; i++) {
for (var j = i+1; j < string.length; j++) {
if (string[i] == string[j]) {
count++;
unique += string[i];
}
}
}
return unique;
}
document.write(unique_char('aaabbbccc'));
The function must be with loop inside a loop; that's why the second for is inside the first.
Fill a Set with the characters and concatenate its unique entries:
function unique(str) {
return String.prototype.concat.call(...new Set(str));
}
console.log(unique('abc')); // "abc"
console.log(unique('abcabc')); // "abc"
Convert it to an array first, then use Josh Mc’s answer at How to get unique values in an array, and rejoin, like so:
var nonUnique = "ababdefegg";
var unique = Array.from(nonUnique).filter(function(item, i, ar){ return ar.indexOf(item) === i; }).join('');
All in one line. :-)
Too late may be but still my version of answer to this post:
function extractUniqCharacters(str){
var temp = {};
for(var oindex=0;oindex<str.length;oindex++){
temp[str.charAt(oindex)] = 0; //Assign any value
}
return Object.keys(temp).join("");
}
You can use a regular expression with a custom replacement function:
function unique_char(string) {
return string.replace(/(.)\1*/g, function(sequence, char) {
if (sequence.length == 1) // if the letter doesn't repeat
return ""; // its not shown
if (sequence.length == 2) // if its repeated once
return char; // its show only once (if aa shows a)
if (sequence.length == 3) // if its repeated twice
return sequence; // shows all(if aaa shows aaa)
if (sequence.length == 4) // if its repeated 3 times
return Array(7).join(char); // it shows 6( if aaaa shows aaaaaa)
// else ???
return sequence;
});
}
Using lodash:
_.uniq('aaabbbccc').join(''); // gives 'abc'
Per the actual question: "if the letter doesn't repeat its not shown"
function unique_char(str)
{
var obj = new Object();
for (var i = 0; i < str.length; i++)
{
var chr = str[i];
if (chr in obj)
{
obj[chr] += 1;
}
else
{
obj[chr] = 1;
}
}
var multiples = [];
for (key in obj)
{
// Remove this test if you just want unique chars
// But still keep the multiples.push(key)
if (obj[key] > 1)
{
multiples.push(key);
}
}
return multiples.join("");
}
var str = "aaabbbccc";
document.write(unique_char(str));
Your problem is that you are adding to unique every time you find the character in string. Really you should probably do something like this (since you specified the answer must be a nested for loop):
function unique_char(string){
var str_length=string.length;
var unique='';
for(var i=0; i<str_length; i++){
var foundIt = false;
for(var j=0; j<unique.length; j++){
if(string[i]==unique[j]){
foundIt = true;
break;
}
}
if(!foundIt){
unique+=string[i];
}
}
return unique;
}
document.write( unique_char('aaabbbccc'))
In this we only add the character found in string to unique if it isn't already there. This is really not an efficient way to do this at all ... but based on your requirements it should work.
I can't run this since I don't have anything handy to run JavaScript in ... but the theory in this method should work.
Try this if duplicate characters have to be displayed once, i.e.,
for i/p: aaabbbccc o/p: abc
var str="aaabbbccc";
Array.prototype.map.call(str,
(obj,i)=>{
if(str.indexOf(obj,i+1)==-1 ){
return obj;
}
}
).join("");
//output: "abc"
And try this if only unique characters(String Bombarding Algo) have to be displayed, add another "and" condition to remove the characters which came more than once and display only unique characters, i.e.,
for i/p: aabbbkaha o/p: kh
var str="aabbbkaha";
Array.prototype.map.call(str,
(obj,i)=>{
if(str.indexOf(obj,i+1)==-1 && str.lastIndexOf(obj,i-1)==-1){ // another and condition
return obj;
}
}
).join("");
//output: "kh"
<script>
uniqueString = "";
alert("Displays the number of a specific character in user entered string and then finds the number of unique characters:");
function countChar(testString, lookFor) {
var charCounter = 0;
document.write("Looking at this string:<br>");
for (pos = 0; pos < testString.length; pos++) {
if (testString.charAt(pos) == lookFor) {
charCounter += 1;
document.write("<B>" + lookFor + "</B>");
} else
document.write(testString.charAt(pos));
}
document.write("<br><br>");
return charCounter;
}
function findNumberOfUniqueChar(testString) {
var numChar = 0,
uniqueChar = 0;
for (pos = 0; pos < testString.length; pos++) {
var newLookFor = "";
for (pos2 = 0; pos2 <= pos; pos2++) {
if (testString.charAt(pos) == testString.charAt(pos2)) {
numChar += 1;
}
}
if (numChar == 1) {
uniqueChar += 1;
uniqueString = uniqueString + " " + testString.charAt(pos)
}
numChar = 0;
}
return uniqueChar;
}
var testString = prompt("Give me a string of characters to check", "");
var lookFor = "startvalue";
while (lookFor.length > 1) {
if (lookFor != "startvalue")
alert("Please select only one character");
lookFor = prompt(testString + "\n\nWhat should character should I look for?", "");
}
document.write("I found " + countChar(testString, lookFor) + " of the<b> " + lookFor + "</B> character");
document.write("<br><br>I counted the following " + findNumberOfUniqueChar(testString) + " unique character(s):");
document.write("<br>" + uniqueString)
</script>
Here is the simplest function to do that
function remove(text)
{
var unique= "";
for(var i = 0; i < text.length; i++)
{
if(unique.indexOf(text.charAt(i)) < 0)
{
unique += text.charAt(i);
}
}
return unique;
}
The one line solution will be to use Set. const chars = [...new Set(s.split(''))];
If you want to return values in an array, you can use this function below.
const getUniqueChar = (str) => Array.from(str)
.filter((item, index, arr) => arr.slice(index + 1).indexOf(item) === -1);
console.log(getUniqueChar("aaabbbccc"));
Alternatively, you can use the Set constructor.
const getUniqueChar = (str) => new Set(str);
console.log(getUniqueChar("aaabbbccc"));
Here is the simplest function to do that pt. 2
const showUniqChars = (text) => {
let uniqChars = "";
for (const char of text) {
if (!uniqChars.includes(char))
uniqChars += char;
}
return uniqChars;
};
const countUnique = (s1, s2) => new Set(s1 + s2).size
a shorter way based on #le_m answer
let unique=myArray.filter((item,index,array)=>array.indexOf(item)===index)

Implement the .split method

I need to implement the .split method in my own way without using prebuilt functions. The method should receive a string divided into 2 sentences by a dot and divide them through a separator.
For example, there is this string:
'word wooord wooooooooord wooooooord. wooooooooord woooooord woooord wooooooooord', separator in this case: '. '
The result should be:
['word wooord wooooooooord wooooooord", "wooooooooord woooooord woooord wooooooooord']
I tried to implement it myself, the first problem I encountered is that the words from the string are added character by character to the new array. The second problem is that the output is still a string even though I declared an array earlier.
function split(str, splitter){
let arrSent = []
for (let i = 0; i < str.length; i++){
if (str[i] != splitter){
arrSent += str[i]
}
}
return arrSent
}
console.log(split('word wooord wooooooooord wooooooord. wooooooooord woooooord woooord wooooooooord', '. '))
Since the delimiter can have more than one character, you need a system to upfront collect a sample of characters (of the same length as the delimiter) to be then compared with the delimiter:
const split = (str, delimiter) => {
// If delimiter is empty string just return an array of characters
if (delimiter === "") return [...str];
const len = delimiter.length;
const iter = str.length - len + 1; // max needed iterations
const arr = [""]; // Prefill it with empty string
let idx = 0; // arr insertion pointer
for (let i = 0; i < iter; i++) {
// Collect len chars from str as a sample
// to compare with the delimiter:
let sample = "";
for (let x = i; x < i + len; x++) {
sample += str[x];
}
const isSplit = sample === delimiter;
const isEnded = i === iter - 1;
if (isSplit) {
i += len - 1; // Consume splitted characters
idx += 1; // Increment arr pointer
arr[idx] = ""; // Prepare the new array key as empty string
} else {
// If loop ended append the entire sample.
// Otherwise, append a single character:
arr[idx] += isEnded ? sample : str[i];
}
}
return arr
}
console.log(split("word. etc", ". "));
console.log(split("word. etc. ", ". "));
console.log(split(". word yep. . etc. ", ". "));
console.log(split("word", ". "));
console.log(split("word", "word"));
console.log(split("word", ""));
console.log(split("", ""));
above, idx (starting at 0) is used as the output's arr insertion pointer. The idx is incremented if the sample matches the delimiter. Also, if there's a match, we need to skip iterations i += len, to not include the delimiter in the output array.
To test, create many examples and right before return arr; use console.log(JSON.stringify(arr) === JSON.stringify(str.split(delimiter))); - it should return true for all the submitted tests.

Add space to string when it includes word in array

I have a function where I am iterating through a given string, alternating the capitalisation of each character and concatenating it to variable alt.
In order to loop through this properly, I have removed spaces from the original string. But I need to add them back at the end of the function.
function alternatingCaps(str) { // 'hello world'
let words = str.toLowerCase().split(' '); // ['hello','world']
str = words.join(''); // 'helloworld'
let alt = '';
for(let i = 0; i < str.length; i++) {
if(i % 2 === 0)
alt += str[i].toUpperCase();
else
alt += str[i].toLowerCase();
}
return alt;
}
console.log(alternatingCaps('hello world'));
/* Output: "HeLlOwOrLd"
Wanted output: "HeLlO wOrLd" */
Once alt contains a string included as a value in the words array, I want to add a space at the end of the word.
Here was my attempt:
words.forEach(function(word) {
if(alt.toLowerCase().includes(word) && word[word.length - 1] === alt[i].toLowerCase())
alt += ' ';
});
It checks if any of the words in the words array are present in the alt string and if the current character iteration of the string corresponds to the last letter in the word. If so, it adds a space to the string.
But this does not work as intended.
> Output: "HeLlO wOr Ld"
> Wanted output: "HeLlO wOrLd"
I also imagine this would cause problems with duplicate letters. How can I accomplish my goal?
You shouldn't join your words. Keep them as separate elements in the words array then you can loop through that array applying you function to each element.
function alternatingCaps(str) { // 'hello world'
let words = str.toLowerCase().split(' '); // ['hello','world']
const alts = words.map(word => capitalizeEvens(word));
return alts.join(' ');
function capitalizeEvens(word) {
let alt = '';
for(let i = 0; i < word.length; i++) {
if(i % 2 === 0)
alt += word[i].toUpperCase();
else
alt += word[i].toLowerCase();
}
return alt;
}
console.log(alternatingCaps('hello world'));
You can iterate through your string one char at a time. Then, check whether the characters is an actual word character. If so, alternate the capitalization, if not, add it to the output as it is:
function altCaps(input) {
var result = '';
var cap = false;
for (var i = 0; i < input.length; i++) {
var c = input[i];
result += /\w/.test(c) ? (cap = !cap) ? c.toUpperCase() : c.toLowerCase() : c;
}
return result;
}
UPDATE: The legible code
function altCaps(input) {
var result = '';
var cap = false;
for (var i = 0; i < input.length; i++) {
var c = input[i];
if (/\w/.test(c)) { // check if char is a "word character" (i.e. letter)
cap = !cap; // toggle next capitalization
if (cap) // if it should capitalize
result += c.toUpperCase(); // add uppercase letter
else
result += : c.toLowerCase(); // add lowercase letter
} else {
result += c; // no letter, so add character as is.
}
}
return result;
}

Any alternative way of using this .length & .split()?

I want to split lower, upper & also the value of textBox without using .split() and also I want
to find the length of the string without using .length. Can anybody solve my problem I am tried but
I cannot find the exact logic for this problem.
var lowercase = "abcdefghijklmnopqrstuvwxyz";
var uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
function Print() {
var input = document.getElementById('demo').value;
document.write(document.getElementById('demo1').innerHTML = toUpper(input));
}
function toUpper(input) {
var upperCase = uppercase.split(""); //other way to split uppercase
var lowerCase = lowercase.split(""); //other way to split lowercase
var inputText = input.split(""); //other way to split input
var newText = "";
var found;
for (var i = 0; i < inputText.length; i++) { //not using .length to other way to find the size of inputText
found = false;
for (var ctr = 0; ctr < lowerCase.length; ctr++) { //not using .length other way to find the size of lowerCase
if (inputText[i] == lowerCase[ctr]) {
found = true;
break;
}
}
if (found) { //true
newText = newText + upperCase[ctr];
} else {
newText = newText + inputText[i];
}
}
return newText;
}
You can count the length of a string using the array function reduce.
Reduce loops over all elements in an array and executes a function you give it to reduce it to one value, you can read more here.
To get reduce working on strings, you need to use Array.from, like this:
Array.from(lowerCase).reduce((sum, carry) => sum + 1, 0) // 26
Reduce accepts a starting argument, which we set to zero here.
This way you do not need to use the split or length functions.
You don't need to check if the input is in a string either, you can use charCodeAt() and fromCharCode().
If you take your input and loop through it using Array.from() then forEach, you can get something which looks like this:
function print() {
const input = document.querySelector('#input').value;
document.querySelector('#target').value = stringToUpper(input);
}
function stringToUpper(input) {
let output = "";
Array.from(input).forEach(char => output += charToUpper(char));
return output;
}
function charToUpper(char) {
let code = char.charCodeAt(0);
code >= 97 && code <= 122 ? code -= 32 : code;
return String.fromCharCode(code);
}
<div>
<input id="input" placeholder="enter text here">
</div>
<button onclick="print()">To Upper</button>
<div>
<input id="target">
</div>
The key line is where we take the output and add the char (as upper) to it:
output += charToUpper(char)
If you don't know about arrow functions, you can read more here
This line:
code >= 97 && code <= 122 ? code -= 32 : code;
is just checking if the char is lower case (number between 97 and 122) and if so, subtracting 32 to get it to upper case.
The reason it is subtract not add is in utf-16, the chars are laid out like this:
ABCDEFGHIJKLMNOPQRTUWXYZabcdefghijklmnopqrtuwxyz
See here for more
I don't know what you mean by "split the value of textBox", but one way to determine the length of a string without using .length would be to use a for...of loop and have a counter increment each time it runs to keep track of the number of characters in the string.
let string = 'boo'
let lengthCounter = 0
for (let char of string) {
lengthCounter++
}
//lengthCounter = 3
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
You can define your own split and length functions:
function mySplit(a){
var counter = 0;
rslt = [];
var val = a[counter];
while(typeof val != "undefined"){
rslt.push(a[counter]);
counter ++;
val = a[counter];
}
return rslt;
}
function myLength(a){
var counter = 0;
var val = a[counter];
while(typeof val != "undefined"){
counter ++;
val = a[counter];
}
return counter;
}
Your function now should be like:
function toUpper(input) {
var upperCase = mySplit(uppercase);
var lowerCase = mySplit(lowercase);
var inputText = mySplit(input);
var newText = "";
var found;
for (var i = 0; i < myLength(inputText); i++) {
found = false;
for (var ctr = 0; ctr < myLength(lowerCase); ctr++) {
if (inputText[i] == lowerCase[ctr]) {
found = true;
break;
}
}
if (found) { //true
newText = newText + upperCase[ctr];
} else {
newText = newText + inputText[i];
}
}
return newText;
}
The simplest way would be to just use the build in function of javascript .toUpperCase() (see example 1). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase
Else if you insist on using a for.loop you may do so aswell (see example two). You do not need the split() function since a string already is an arrayof characters. Also be aware that not all characters in the web have lowercase counterparts, so the logic itself is flawed.
//REM: This lines are not required.
/*
var lowercase = "abcdefghijklmnopqrstuvwxyz";
var uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
function Print() {
var input = document.getElementById('demo').value;
document.write(document.getElementById('demo1').innerHTML = toUpper(input));
}
*/
//REM: Version 1 (using string.toUpperCase())
(function toUpper1(input){
var tReturn = (input || '').toUpperCase();
console.log('toUpper1', tReturn);
return tReturn
}('abcDEFghiJKL'));
//REM: Version 2 (using your way)
(function toUpper2(input){
var tReturn = '';
if(input && input.length){
for(let i=0, j=input.length; i<j; i++){
tReturn += (input[i] === input[i].toLowerCase()) ? input[i].toUpperCase() : input[i]
}
};
console.log('toUpper2', tReturn);
return tReturn
}('abcDEFghiJKL'));

Get the next key from array from string with symbols

I'm working on a simple but difficult problem for me right now, I'm use to work in jQuery but need this to be done in Javascript.
So simple as it is, the user inputs a string lets say:
"hey, wanna hang today?". It should output the next character in my array, so it would be like this: "ifz, xboob iboh upebz?".
And I have tried everything I can come up with. Hopefully some of you guys see the problem right away.
I have set up a short jsFiddle that shows similar to what I got.
function gen() {
var str = document.getElementById('str').value,
output = document.getElementById('output');
var alph = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','æ','ø','å','a'];
for (var i=0;i<str.length;i++) {
var index = str[i].charAt(0),
e = alph.indexOf(index);
console.log(alph[e + 1]);
output.innerHTML += alph[e + 1];
}
}
If you only want to skip to next letter with those chars and leave the others like space and ? as they are:
var index = str[i].charAt(0),
e = alph.indexOf(index);
if(e == -1){
output.innerHTML += index;
}else{
output.innerHTML += alph[e + 1];
}
Update: using #David Thomas method, you could do the following: (wouldnt work for 'å' though)
var index= str[i].toLowerCase().charCodeAt(0);
if((index > 96 && index < 123)){ // a to z
output.innerHTML += String.fromCharCode(str[i].charCodeAt(0)+1);
}else{
output.innerHTML += str[i];
}
}
I'd personally recommend the following approach, which should work with any alphabet for which there's a Unicode representation and, somewhat importantly, doesn't require a hard-coded array of letters/punctuation for each language:
function gen() {
var str = document.getElementById('str').value,
strTo = '',
output = document.getElementById('output');
for (var i = 0; i < str.length; i++) {
strTo += String.fromCharCode(str[i].charCodeAt(0) + 1);
}
output.textContent = strTo;
}
// hey, wanna hang today? -> ifz-!xboob!iboh!upebz#
JS Fiddle demo.
References:
String.prototype.charCodeAt().
String.prototype.fromCharCode().
Why does gen(',') === 'a'?
var alph = 'abcdefghijklmnopqrstuvwxyz';
var e = alph.indexOf(',');
console.log(e);
// -1
console.log(alph[e + 1]);
// 'a'
You need to take this case into account; otherwise, any characters that aren't in alph will map to 'a'.
(I see that you've also duplicated 'a' at the start and end of alph. This works, though it's more common either to use the modulus operator % or to check explicitly if e === alph.length - 1.)
You just have to add an array with the non respected characters:
var ex = ['?','!',' ','%','$','&','/']
In whole
for (var i=0;i<str.length;i++) {
var index = str[i].charAt(0)
if (alph.indexOf(index) >-1) {
var e = alph.indexOf(index);
output.innerHTML += alph[e + 1];
} else {
var e = index;
output.innerHTML += e;
}
}
JSFIDDLE: http://jsfiddle.net/TRNCFRMCN/hs15f0kd/8/.

Categories

Resources