Writing a text scrambling algorithm backwards - javascript

Explanation
I'm building a simple word scrambler/encoder in for fun. Given a seed and the text to convert, the algorithm to turn the original string illegible is to:
Loop through the characters of a user-given seed, say "lkj"
Loop through the characters of the text to convert, say "hello"
Get the index of both according to a list of accepted characters; so if [a-z] were characters that could be scrambled, and the loop was on index 0 for both the seed and conversion text, I'd get l = index 11 and h = index 7
Add those two indices together. 11 + 7 = 18. If the index exceeds the length of the accepted list, decrease that length from the new index (eg. 33 - 26).
Get the character corresponding to index 18 on the accepted list. s is at index 18
Repeat until all the conversion characters have been looped through, returning the current seed index to 0 if the text exceeds the length of the seed
We end up with "souwy".
The algorithm to decode text (should be with the same seed) is just to do everything backwards. Start at the end of the string, start looping through the seed backwards at the index that it would stop at (seed.length % text.length), subtract indices instead of adding, then reverse the resulting string. So if we input "lkj" as the seed and "souwy" as the conversion text, we'll get "hello" back upon decoding.
Problem
The decoding seems to work only sometimes. With some combination of the seed and conversion text, the algorithm fails--but I have no idea what it could be. For example, using the following information:
Seed: lkj
Input: Hey guys! My email is yay#someDomain.com, but don't send me anything U_U
Decoding fails. However, if an 'x' is added to the end of the input, it works. What could be going wrong?
Fiddle

Looks like you have an off by one error when the length of your message is a multiple of the length of your seed.
The problem line was:
var is_currSeed = (numLastWords > 0) ? (numLastWords - 1) : 0
should be changed to:
var is_currSeed = (numLastWords > 0) ? (numLastWords - 1) : seed.length - 1;
Here's a working version.

Related

Finding volume and item count using regular expressions

I am currently building a JavaScript web scraper for a grocery store that processes a title of a product and then returns the item count, volume and price per litre of a product. Most of the product titles look something like this:
Coca cola (vanilla flavour) 12 x 330 mL
In order to obtain meta data about this product, I have written a Regular Expression. It will look for look for a word boundary followed by a 1 or 2 digit number, whitespace, the string 'x', another whitespace and finally a 1, 2 or 3 digit number:
const filter = new RegExp(/\b\d{1,2}\sx\s\d{1,3}/);
I then test each result for a match with the Regular Expression and then calculate the item count, item volume, volume in litres and then the price per litre.
if (result.title.match(filter)) {
result.itemCount = parseInt(result.title.match(/\d{1}\s/));
result.itemVolume = parseInt(result.title.match(/\d{2,3}\s/));
result.litreVolume = (result.itemCount * result.itemVolume) / 1000;
result.pricePerLitre = +(result.price / result.litreVolume).toFixed(2);
} else {
result.itemCount = 1;
result.itemVolume = parseInt(result.title.match(/\d{2,3}\s/));
result.litreVolume = result.itemVolume / 1000;
result.pricePerLitre = +(result.price / result.litreVolume).toFixed(2);
}
90% of the results look good, but sometimes I get unexpected results. For example:
an item count of NaN, which may have to do with the fact that some titles contain several more numbers (Coca Cola (4-Way) 12 x 330 mL))
a volume of Infinity
a price per litre that is way too high
Clearly I am doing something wrong with my approach to calculating the desired meta data. What would be a better way of doing calculations with RegEx? Am I missing something that would make my calculations less prone to errors?
If i understand correctly filter \b\d{1,2}\sx\s\d{1,3} works, but your sub filters do not (\d{1}\s)...
I only used to using regex in c# but, i saw you could use groups in java also.
change your pattern to (\b\d{1,2})\sx\s(\d{1,3}). When you put brackets in your regex, that part becomes a group that you can acces afterwards.
As i said, i haven't used java in a few years, but i picked this code snippet from the web. It shows how to use groups in java. As pattern you should use the (\b\d{1,2})\sx\s(\d{1,3}). If it is the same as in c# group(0) is the whole result, group(1) is your first actual group, group(2) is the second.
// Create a Pattern object
Pattern r = Pattern.compile(pattern);
// Now create matcher object.
Matcher m = r.matcher(line);
if (m.find( )) {
System.out.println("Found value: " + m.group(0) );
System.out.println("Found value: " + m.group(1) );
}
I think you can write it with less code than stated above, but you get the picture ;-)

How do I replace a letter that is 15 chars ahead of each other?

I am trying to replace each character a user inputs that is 15 letters ahead in the alphabet. For example, if a user inputs the word A, then it would output "P".
the word "AB" would output "PQ" and so on with every word.
I've tried the following code but it is not working. I am thinking a loop may be the answer, but if anyone can think of some better ideas let me know.
alphabetArray = ["abcdefghijklmnopqrstuvwyxz"];
function jumpAhead15(aString){
aString.replace(alphabetArray[0][aString + 15]);
}
jumpAhead15("hi");
You could take the string as iterable and get an array of characters and replace any character by getting the index and the offset, adjusted by the remainder of the length of the alphabet.
The reminder operator % returns the rest of a division:
The remainder operator returns the remainder left over when one operand is divided by a second operand. It always takes the sign of the dividend.
The function is here to keep the index for the getting the character of alphabet in a valid range. For example by taking index 20 and the wanted shift of 15, you get 35 which is not an index of alphabet. by using the remainder operator you get the value of 9 which is the rest of 35 / 26 = 1 rest 9
Then return the character and join the array to a final string.
Method used:
Array.from, which takes two arguments, one iterable or an object with a length property and a mappring function, which is called for every element of the new array. (c is the first parameter of the callback and denotes here a single character)
arrow function as callback for Array.from
function jumpAhead15(aString) {
var alphabet = "abcdefghijklmnopqrstuvwyxz";
return Array
.from(
aString,
c => alphabet[(alphabet.indexOf(c) + 15) % alphabet.length]
)
.join('');
}
console.log(jumpAhead15("hi"));
console.log(jumpAhead15("ab"));
With alphabetArray as above, try:
aString.split("").map(letter => alphabetArray[(alphabetArray.indexOf(letter) + 15) % 26]).join("");
That is: convert the string to an array of 1-letter strings, replace each letter by the one 15 places further along (wrapping if necessary, hence the modulus operator %), then join the letters back together into one string.
If you find the answer from Nina hard to understand, then this one may not be for you either, but here is a function that is built on a more generic letter rotation scheme. It also handles differences in case:
const rotate = (configs) => (str) => str.split('').map(
c => configs.reduce(
(s, [letters, offset]) => {
const idx = letters.indexOf(s);
return idx < 0 ? s : letters[(idx + offset) % letters.length]
},
c
)
).join('')
const by15 = rotate([
[[...'abcdefghijklmnopqrstuvwxyz'], 15],
[[...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'], 15]
])
console.log(by15('ab')) //~> 'pq'
console.log(by15('aB')) //~> 'pQ'
console.log(by15('Hello, world!')) //~> 'Wtaad, ldgas!'
This uses the same basic technique with the modulus operator, modified slightly to return the original character if it's not in our list. We do this for each alphabet supplied, in this case, the upper- and lower-case letters. This only works if the different alphabets have no overlap. We pass the offset together with each alphabet, in case you want to, for instance, rotate digits by 3 and letters by 15. If you didn't want to do this, you could move the offset out of the configs parameter (in which case it might be called alphabets) and make the single offset a second parameter for rotate.
Note the structure: rotate is an arrow function which returns an arrow function. So to get a useful function out of it, you need to call it with the alphabet configuration. The function returned just takes a string and returns the rotated string.
This is not the most efficient solution, as you have to loop through each alphabet, even once you've found it. Since I wouldn't expect it to be used with many alphabets, this seems a good tradeoff for relatively clean code. But one could easily write a version which searched through until it found an alphabet that contained your character and then worked on that one. This is left as an exercise for the reader.

How can I find the missing integer in a string of random non-repeating integers in a range

I'm having trouble even beginning to think of how to do this.
I need to find a missing number in a string of random numbers that don't have separators.
Here's an example: 14036587109. In this example, the missing number is 2, the range is 0-10 (inclusive).
How can I write a JavaScript/Node.JS program to solve this?
The part I can't figure out is how the program separates the numbers; In the example above, how would the program know that the number 10 (before the last number) isn't the numbers 1 and 0.
There are two things we know about the missing integer: the total number of digits in the input tells us the number of digits in the missing integer, and (as #samgak mentioned in a comment) counting the occurrences of each digit in the input tells us which digits the missing integer is made of. This may give us a quick path to the solution, if one of the permutations of those digits is missing from the input. If it doesn't, then:
Find the integers from highest to lowest number of digits; if the range is e.g. 0-999, then search the 3-digit integers first, then 2, then 1.
If an integer is only present at one location in the input, mark it as found, and remove it from the input.
Then, start again with the longest integers that haven't been found yet, and look at the ones that are present at two locations; try both options, and then check whether all other integers that rely on the digits we're using are also present; e.g. if 357 is present at two locations:
... 1235789 ... 2435768 ...
357 357
23 43
123 243
235 435
578 576
78 76
789 768
When trying the first location for the 357, check whether there is another possibility for 23, 123, 235, 578, 78, and 789. For the second location, check 43, 243, 435, 576, 76 and 768.
If these checks show that only one of the options is possible, mark the number as found and remove it from the input.
Go on to do this for shorter integers, and for integers that are present at 3, 4, ... locations. If, after doing this to a certain point, there is still no result, you may have to recursively try several options, which will quickly lead to a huge number of options. (With especially crafted large input, it is probably possible to thwart this method and make it unusably slow.) But the average complexity with random input may be decent.
Actually, when you find an integer that is only present in one location in the input, but it is a permutation of the missing digits, you should not remove it, because it could be the missing integer. So the algorithm might be: remove all integers you can unequivocally locate in the input, then try removing all possible missing integers one by one, and look for inconsistencies, i.e. other missing numbers that don't have the correct length or digits.
It's all a question of heuristics, of course. You try something simple, if that doesn't work you try something more complicated, if that doesn't work, you try something even more complicated... and at each step there are several options, and each one could be optimal for some input strings but not for others.
E.g. if the range is 0-5000, you'd start by marking the 4-digit integers that are only present at one location. But after that, you could do the same thing again (because integers that were present twice could have had one of their options removed) until there's no more improvement, or you could check integers that are present twice, or integers that are present up to x times, or move on to 3-digit integers... I don't think there's a straightforward way to know which of these options will give the best result.
This solution should work for any input string and any start/end range:
We can think about the numbers in the string as a pool of digits that we can choose from. We start at startRange and go through to endRange, looking for each number along the way in our pool of digits.
When we find a number that can be composed from our pool of digits, we delete those digits from our pool of digits, as those digits are already being used to form a number in our range.
As soon as we come across a number that cannot be composed from our pool of digits, that must be the missing number.
const str = "14036587109"; // input
const numsLeft = str.split("").map(num => parseInt(num)); // array of numbers
const startRange = 0;
const endRange = 10;
for(let i = startRange; i <= endRange ; i++) {
// check if number can be formed given the numbers left in numsLeft
const numFound = findNum(numsLeft, i);
if(!numFound) {
console.log("MISSING: " + i); // prints 2
break;
}
}
function findNum(numsLeft, i) {
// array of digits
const numsToFind = String(i).split("").map(num => parseInt(num));
// default is true, if all digits are found in numsLeft
let found = true;
numsToFind.forEach(num => {
// find digit in numsLeft
const numFoundIndex = numsLeft.indexOf(num);
if(numFoundIndex < 0) {
// digit was not found in numsLeft
found = false;
return;
} else {
// digit was found; delete digit from numsLeft
numsLeft.splice(numFoundIndex, 1);
}
});
return found;
}
var input = '10436587109';
var range = [10,9,8,7,6,5,4,3,2,1,0];
var expr1 = new RegExp(range.join('|'),'g');
var expr2 = new RegExp('[0-9]','g');
var a = input.match(expr1).map(Number).concat(input.match(expr2).map(Number));
var x = range.filter(function(i){ return a.indexOf(i)===-1; });

binary search for closest index/ index's to a value with O(log n)

I have sorted array (duplicates can exist) of objects with dateTime in milliseconds as a member to the objects, I want to get the index/index's of the array which has the least dateTime difference from currentTime. I thought of using binary search but it needs the key to be present in the array. I can use simple for loop but it will be O(n) is there a efficient binary search variant to get index/index's with O(log n) efficiency?.
I want to get the index/index's of the array which has the least dateTime difference from currentTime
So you want to find the largest value less than or equal to currentTime and the smallest value higher than or equal to currentTime. Your answer has to be one of these.
For the first one, you can write your binary search like this:
while left < right:
m = left + (right - left) / 2
if array[m] <= target: # using <= here to save the right index
# in store in case the key exists
left = m + 1
store = m
else:
right = m
After this runs, store will contain the last index lower than or equal to target. Then you can check:
if array[store] == target:
you found the target exactly
else:
check array[store] and array[store + 1] (if not out of bounds) and pick the best.

Chunk a string every odd and even position

I know nothing about javascript.
Assuming the string "3005600008000", I need to find a way to multiply all the digits in the odd numbered positions by 2 and the digits in the even numbered positions by 1.
This pseudo code I wrote outputs (I think) TRUE for the odd numbers (i.e. "0"),
var camid;
var LN= camid.length;
var mychar = camid.charAt(LN%2);
var arr = new Array(camid);
for(var i=0; i<arr.length; i++) {
var value = arr[i]%2;
Alert(i =" "+value);
}
I am not sure this is right: I don't believe it's chunking/splitting the string at odd (And later even) positions.
How do I that? Can you please provide some hints?
/=================================================/
My goal is to implement in a web page a validation routine for a smartcard id number.
The logic I am trying to implement is as follows:
· 1) Starting from the left, multiply all the digits in the odd numbered positions by 2 and the digits in the even numbered positions by 1.
· 2) If the result of a multiplication of a single digit by 2 results in a two-digit number (say "7 x 2 = 14"), add the digits of the result together to produce a new single-digit result ("1+4=5").
· 3) Add all single-digit results together.
· 4) The check digit is the amount you must add to this result in order to reach the next highest multiple of ten. For instance, if the sum in step #3 is 22, to reach the next highest multiple of 10 (which is 30) you must add 8 to 22. Thus the check digit is 8.
That is the whole idea. Google searches on smartcard id validation returned nothing and I am beginning to think this is overkill to do this in Javascript...
Any input welcome.
var theArray = camid.split(''); // create an array entry for each digit in camid
var size = theArray.length, i, eachValue;
for(i = 0; i < size; i++) { // iterate over each digit
eachValue = parseInt(theArray[i], 10); // test each string digit for an integer
if(!isNaN(eachValue)) {
alert((eachValue % 2) ? eachValue * 2 : eachValue); // if mod outputs 1 / true (due to odd number) multiply the value by 2. If mod outputs 0 / false output value
}
}
I discovered that what I am trying to do is called a Luhn validation.
I found an algorithm right here.
http://sites.google.com/site/abapexamples/javascript/luhn-validation
Thanks for taking the time to help me out. Much appreciated.
It looks like you might be building to a Luhn validation. If so, notice that you need to count odd/even from the RIGHT not the left of the string.

Categories

Resources