Check for range of characters in a random string - javascript

I have this function to generate a random password.
My question is How do I ensure that a selected range of letters/sign/numbers is included in the result?
As an example, if I select that there must be numbers or signs in the result string, how do I check if there is at least one number or sign in the result. I have toughed about looping through numbers or signs for each member of the result, and then break the loop if I meet a character I am testing for.
The code snippet works, but without validating the result
function generatePassword() {
var base = "";
var result = "";
var latinLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
var cyrillicLetters = "БбВвГгДдЕеЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЬьЮюЯя"
var greekLetters = "ΑαΒβΓγΔδΕεΖζΗηΘθΙιΚκΛλΜμΝνΞξΟοΠπΡρΣσςΤτΥυΦφΧχΨψΩω"
var numbersLetters = "1234567890"
var signsLetters = "/|()1{}[]?-_+~<>!I;:,^`.$#B%&WM*"
var armenianLetters = "աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆուև"
var hangulLetters = "ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎㅏㅑㅓㅕㅗㅛㅜㅠㅡㅣ"
var nordicLetters = "AaÁáBbCcDdÐðEeÉéFfGgHhIiÍíJjKkLlMmNnOoÓóPpRrSsTtUuÚúVvWwXxYyÝýZzÞþÆæÖöZzÄäØøÅå"
var arabicLetters = "ءي و ه ن م لك ق ف غ ع ظ ط ض ص ش س ز ر ذ د خ ح ج ث ت ب ا"
var georgianLetters = "აბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰ"
var ethiopianLetters = "ሀለሐመሠረሰቀበተኀነአከወዐዘየደገጠጸፀፈጰፐ"
var thaanaLetters = "ހށނރބޅކއވމފދތލގސޑޒޓޔޕޖޗޘޙޚޛޜޝޞޟޠޡޢޣޤޥަ ީ ު ޫ ެ ޭ ޮ ޯޱ"
var hanziLetters = "ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ ゙ ゛゜ゝゞゟ"
// input
var length = document.getElementById('pwLength').value
var latin = document.getElementById('latinChecked')
var numbers = document.getElementById('numbersChecked')
var signs = document.getElementById('signsChecked')
var nordic = document.getElementById('nordicChecked')
var cyrillic = document.getElementById('cyrillicChecked')
var greek = document.getElementById('greekChecked')
var armenian = document.getElementById('armenianChecked')
var hangul = document.getElementById('hangulChecked')
var arabic = document.getElementById('arabicChecked')
var georgian = document.getElementById('georgianChecked')
var ethiopian = document.getElementById('ethiopianChecked')
var thaana = document.getElementById('thaanaChecked')
var hanzi = document.getElementById('hanziChecked')
// build source string
if (arabic.checked) { base = base + arabicLetters; }
if (latin.checked) { base = base + latinLetters; }
if (signs.checked) { base = base + signsLetters; }
if (greek.checked) { base = base + greekLetters; }
if (numbers.checked) { base = base + numbersLetters; }
if (armenian.checked) { base = base + armenianLetters; }
if (hangul.checked) { base = base + hangulLetters; }
if (numbers.checked) { base = base + numbersLetters; }
if (nordic.checked) { base = base + nordicLetters; }
if (cyrillic.checked) { base = base + cyrillicLetters; }
if (numbers.checked) { base = base + numbersLetters; }
if (georgian.checked) { base = base + georgianLetters; }
if (ethiopian.checked) { base = base + ethiopianLetters; }
if (thaana.checked) { base = base + thaanaLetters; }
if (hanzi.checked) { base = base + hanziLetters; }
// fill result
for (let i = 0; i < length; i++) {
var rand = Math.floor(Math.random() * base.length);
result = result + base.charAt(rand);
}
// write to html
document.getElementById("productX").innerHTML = result;
}

You can use this process:
Select exactly one random character from each character set that was selected
Select the remaining needed characters from all available characters
Concatenate all of the above and shuffle randomly
Here is an implementation:
// Some utility functions on Math.random
const randInt = end => Math.floor(Math.random() * end);
const randomOf = (array, length=1) => Array.from({length}, () =>
array[randInt(array.length)]
);
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = randInt(i + 1);
let temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
const groups = {
latin: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
cyrillic: "БбВвГгДдЕеЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЬьЮюЯя",
greek: "ΑαΒβΓγΔδΕεΖζΗηΘθΙιΚκΛλΜμΝνΞξΟοΠπΡρΣσςΤτΥυΦφΧχΨψΩω",
numbers: "1234567890",
signs: "/|()1{}[]?-_+~<>!I;:,^`.$#B%&WM*",
armenian: "աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆուև",
hangul: "ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎㅏㅑㅓㅕㅗㅛㅜㅠㅡㅣ",
nordic: "AaÁáBbCcDdÐðEeÉéFfGgHhIiÍíJjKkLlMmNnOoÓóPpRrSsTtUuÚúVvWwXxYyÝýZzÞþÆæÖöZzÄäØøÅå",
arabic: "ءي و ه ن م لك ق ف غ ع ظ ط ض ص ش س ز ر ذ د خ ح ج ث ت ب ا",
georgian: "აბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰ",
ethiopian: "ሀለሐመሠረሰቀበተኀነአከወዐዘየደገጠጸፀፈጰፐ",
thaana: "ހށނރބޅކއވމފދތލގސޑޒޓޔޕޖޗޘޙޚޛޜޝޞޟޠޡޢޣޤޥަ ީ ު ޫ ެ ޭ ޮ ޯޱ",
hanzi: "ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ ゙ ゛゜ゝゞゟ",
};
function generatePassword(length, charGroups) {
if (charGroups.length > length) {
throw "Required length incompatible with number of character groups";
}
// Select one from each set, and take the rest from any in those sets. Then shuffle.
return shuffle([
...charGroups.flatMap(group => randomOf(groups[group])),
...randomOf(charGroups.map(group => groups[group]).join(""), length - charGroups.length)
]).join("");
}
// I/O handing:
function clickHandler() {
// Get inputs
const length = +document.getElementById('pwLength').value;
const selected = Object.keys(groups).filter(group => document.getElementById(group + "Checked").checked);
// Apply random generation based on these parameters
const result = generatePassword(length, selected);
// Output:
document.getElementById("productX").textContent = result;
}
document.getElementById("generate").addEventListener("click", clickHandler);
#pwLength { width: 4em }
#productX { font-family: monospace }
Length: <input id="pwLength" type="number" value="16">
<table><tr>
<td><input id="latinChecked" type="checkbox" checked>Latin</td>
<td><input id="cyrillicChecked" type="checkbox">Cyrillic</td>
<td><input id="greekChecked" type="checkbox">Greek</td>
<td><input id="numbersChecked" type="checkbox" checked>Numbers</td>
</tr><tr>
<td><input id="signsChecked" type="checkbox" checked>Signs</td>
<td><input id="armenianChecked" type="checkbox">Armenian</td>
<td><input id="hangulChecked" type="checkbox">Hangul</td>
</tr><tr>
<td><input id="nordicChecked" type="checkbox">Nordic</td>
<td><input id="arabicChecked" type="checkbox">Arabic</td>
<td><input id="georgianChecked" type="checkbox">Georgian</td>
</tr><tr>
<td><input id="ethiopianChecked" type="checkbox">Etheopian</td>
<td><input id="thaanaChecked" type="checkbox">Thaana</td>
<td><input id="hanziChecked" type="checkbox">Hanzi</td>
</tr></table>
<button id="generate">Generate password</button><br>
Password: <span id="productX"></span>

I will add this method, to check if a selected sequences of letters is included
if (latin.checked) {
var check = 0;
for (let i = 0; i > result.length; i++) {
if (latinLetter.includes.includes(result.charAt(i))) {
i = result.length + 1;
check++;
}
if (check == 0) { generatePassword(); }
}
}
Regarding avoiding repeated characters as #Scott mentioned
for (let i = 0; i < length; i++) {
var rand = Math.floor(Math.random() * base.length);
result = result + base.charAt(rand);
xLetter = xLetter.slice(rand, rand + 1);
}
But i guess that it would be better to just avoid repeating the last character
var last;
for (let i = 0; i < length; i++) {
var rand = Math.floor(Math.random() * base.length);
if ( last == base.charAt(rand) ) {
var rand = Math.floor(Math.random() * base.length);
}
if ( last == base.charAt(rand).toLowerCase() ) {
var rand = Math.floor(Math.random() * base.length);
}
if ( last == base.charAt(rand).toUpperCase() ) {
var rand = Math.floor(Math.random() * base.length);
}
last = base.charAt(rand);
// can be equal to the last character, but the probability is low
result = result + base.charAt(rand);
}
if (latin.checked) {
var check = 0;
for (let i = 0; i > result.length; i++) {
if (latinLetter.includes.includes(result.charAt(i))) {
i = result.length + 1;
check++;
}
if (check == 0) { generatePassword(); }
}
}
// is upper
var upperCheck = 0;
for (let i = 0; i > result.length; i++) {
if (result.charAt(i) == result.charAt(i).toUpperCase()) {
i = result.length + 1;
upperCheck++;
}
if (upperCheck == 0) { generatePassword(); }
}
// is upper
var lowerCheck = 0;
for (let i = 0; i > result.length; i++) {
if (result.charAt(i) == result.charAt(i).toLowerCase()) {
i = result.length + 1;
lowerCheck++;
}
if (lowerCheck == 0) { generatePassword(); }
}

Related

Convert Binary/Hex to Base91 / Ascii85 / Base64

I need an JS encoder to base91 or Ascii85 for a binary number. I do have a google sheet, which looks likes as follows:
Code is:
function columnToLetter(column)
{
//https://stackoverflow.com/a/21231012/14226613
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
function onEdit(e){
var activeSheet = e.source.getActiveSheet();
if (activeSheet.getActiveCell().getRow() == 3) {
var spreadsheet = SpreadsheetApp.getActive();
var str = ''
for (i=0; i < 32; i++) {
var colName = columnToLetter(i+1)
if (spreadsheet.getRange(colName+ '3').isChecked()) {
str = str + '1'
} else {
str = str + '0'
}
}
let number = parseInt(str,2)
let hexx = ((+number).toString(16))
spreadsheet.getRange('R7').setValue(hexx);
}
}
Currently an encoding to Hex does work well - but I need a more efficient way to encode this binary flags.
Purpose: this encoded pattern might be in the future a part of a product / spare part name, where I have 5-6 characters maximum (as like in the 80's :D).
Ascii85 would be great, hence the represenation of 'ffffffff' is 's8W-!' where i would save 3 characters. For testing/encoding I used cryptii.
The solution should be pure JS without external dependencies/requires and/or should be able in run Google's environment.
Do you know any libs which I could use for this purpose? Base91 would be also fine - as long as we have printable characters.
The perfect solution would be configurable JS encoder/decoder - where the patterns respectively the characters for encoding could be preselected.
Update:
Figured out that Ascii85 or Base91 or not that good for announcing codes per phone - hence you don't wanna find all characters easily on your keyboard.
It is true that base64 is less efficient but with adapting the requirements I was able to find a solution with max. 4-5 characters after experimenting a few days.
I am going to try to answer my own question - see below.
Updated requirements:
16 bits for payload
4 bits (number 1..15) for family/recipe/type selection
4 bits for CRC4
base64 encoded without external dependencies and with adjustable alphabet
This solution works for me. The whole 3Byte is been encoded into 4 characters. I adapted the alphabet for replacing some missleading characters (0,O,i,l,1 etc.).
The base64 padding ('=') is been removed after encoding and will be added before decoding inside the functions. CRC4 is not perfect - better than no CRC :)
I am happy for any feedback, suggestions further optimisations. Thanks.
Google Sheet frontend:
Another tab in this documents where you can define the variations/families/recipes:
Here comes the code for the app script (special credits to #Kamil Kiełczewski for his base64 snippert):
// Convert a hex string to a byte array
function hexToBytes(hex) {
// https://stackoverflow.com/a/34356351/14226613
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
function hexToBytes2(hexString) {
//https://stackoverflow.com/a/62365404/14226613
return hexString.match(/.{1,2}/g).map(x=> +('0x'+x));
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
// https://stackoverflow.com/a/34356351/14226613
for (var hex = [], i = 0; i < bytes.length; i++) {
var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
hex.push((current >>> 4).toString(16));
hex.push((current & 0xF).toString(16));
}
return hex.join("");
}
function hex2bin(hex, fill = false){
//https://stackoverflow.com/a/45054052/14226613
let bin = (parseInt(hex, 16).toString(2)).padStart(8, '0');
if (fill) {
//https://stackoverflow.com/a/27641835/14226613
bin = "000000000000000000000000".substr(bin.length) + bin;
}
return bin
}
function b64(){
const abc = "ABCDEFGH!JKLMN=PQRSTUVWXYZabcdefghijk:mnopqrstuvwxyz+-$%23456789"; // base64 alphabet
function encode (byteArray) {
//https://stackoverflow.com/a/62365404/14226613
const bin = n => n.toString(2).padStart(8,0); // convert num to 8-bit binary string
const l = byteArray.length
let result = '';
for(let i=0; i<=(l-1)/3; i++) {
let c1 = i*3+1>=l; // case when "=" is on end
let c2 = i*3+2>=l; // case when "=" is on end
let chunk = bin(byteArray[3*i]) + bin(c1? 0:byteArray[3*i+1]) + bin(c2? 0:byteArray[3*i+2]);
let r = chunk.match(/.{1,6}/g).map((x,j)=> j==3&&c2 ? '=' :(j==2&&c1 ? '=':abc[+('0b'+x)]));
result += r.join('');
}
//remove padding - http://jsfiddle.net/bicycle/c49fgz8x/
result = result.replace(/={1,2}$/, '');
return result;
}
function decode (str) {
//https://stackoverflow.com/a/62364519/14226613
let result = [];
//add padding http://jsfiddle.net/bicycle/c49fgz8x/
str = str + Array((4 - str.length % 4) % 4 + 1).join('=');
for(let i=0; i<str.length/4; i++) {
let chunk = [...str.slice(4*i,4*i+4)]
let bin = chunk.map(x=> abc.indexOf(x).toString(2).padStart(6,0)).join('');
let bytes = bin.match(/.{1,8}/g).map(x=> +('0b'+x));
result.push(...bytes.slice(0,3 - (str[4*i+2]=="=") - (str[4*i+3]=="=")));
}
return result;
}
return {
encode: encode,
decode: decode
}
}
var crc4 = function(data) {
//https://gist.github.com/bryc/5916452ad0d1ef5c39f1a3f19566d315
var POLY = 0xC, INIT = 0, XOROUT = 0;
for(var crc = INIT, i = 0; i < data.length; i++) {
crc = crc ^ data[i];
for(var j = 0; j < 8; j++) {
crc = crc & 1 ? crc >>> 1 ^ POLY : crc >>> 1;
}
}
return (crc ^ XOROUT) & 0xF;
};
function get_family(input, sheet, range) {
//get array with family names
let familiesValNames =sheet.getRange(range).getValues();
let obj = {};
for (i=0; i < familiesValNames.length; i++) {
if ( //if input is string - loop through array and find the corresponding number
(typeof input === 'string' && input == familiesValNames[i]) ||
//if it is a number between 1..15
(typeof input === 'number' && input == (i+1))) {
obj.number = i + 1;
//first values are in row 4 (=offset)
let sRange = 'A' + (4 + i) + ':P' + (4 + i)
obj.names = sheet.getRange(sRange).getValues();
break;
}
}
return obj
}
function set_family_object (sheet, range, fixed = false){
let famtypes={1:{family:["Spares ACME 1"],types:[["spare","Alpha v2","Alpha v1","Alpha v0","spare","Beta v2","Beta v1","Beta v0","spare","Gamma v2","Gamma v1","Gamma v0","spare","Delta v2","Delta v1","Delta v0"]]},2:{family:["Spares ACME 2"],types:[["spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","Omega v2","Omega v2","Omega v1","Omega v0"]]},3:{family:["ACME Tools"],types:[["p612 lcp2","p612 lcp1","p612 lcp0","p512 lcp2","p512 lcp1","p512 lcp0","p671 lcp2","p671 lcp1","p671 lcp0","p471 lcp2","p471 lcp1","p471 lcp0","blm v2","blm v1","blm v0","common"]]},4:{family:["Animals"],types:[["Sea Otter","Cat","Panda","Fox","Dog","Lemur","Penguin","Mulgara","Degu","Kiwi","Koala","Monkey","Whale","Oribi","Chimpanzee","Deer"]]},5:{family:["Male Names"],types:[["James","John","Robert","Michael","Willian","David","Richard","Thomas","Charles","Daniel","Matthew","Donald","Mark","Brian","Edward","George"]]},6:{family:["Female Names"],types:[["Olivia","Emma","Ava","Sophia","Isabella","Charlotte","Amelia","Mia","Harper","Evelyn","Emily","Ella","Camila","Luna","Mila","Aria"]]},7:{family:["Car Brands"],types:[["Skoda","Renault","Citroen","Dacia","BMW","Audi","VW","Ford","Peugeot","Honda","Hyundai","Toyota","Kia","Opel","Porsche","Tata"]]},8:{family:["Horror Movies"],types:[["Wrong Turn","Descent","Hostel","Vacancy","Joy ride","Joy Ride","Midsommar","Cave","Eden Lake","Frozen","Ruins","Turistas","Hills have Eyes","Cabin of Woods","Soon the Darkness","Deep"]]},9:{family:["Things for Salad"],types:[["Lettuce","Cucumber","Cheese","Carrot","Tomato","Spinach","Crouton","Bacon","Egg","Onion","Chicken","Avocado","Pepper","Cabbage","Olive Oil","Feta"]]},10:{family:["TV Scientist"],types:[["Spock","Gaius Baltar","Leonard McCoy","Dr Bruce Banner","MacGyver","Dr Krieger","Walter White","Dexter","Lex Luthor","Walter Bishop","Data","The Doctor","Dr Quest","Dana Scully","Jimmy Neutron","Howard Wolowitz"]]},11:{family:["Funny Movies"],types:[["Spaceballs","Airplane!","Monty Python","Blazin Saddles","Lego Movie 2","Shrek","Naked gun","Austin Powers","Shaun of the Dead","Finding Nemo","The Big Lebowski","Dumb and Dumber","Simpsons","Toy Story","South Park","Life of Brian"]]},12:{family:["Actors"],types:[["Jamie Foxx","Val Kilmer","Christian Bale","Cate Blanchett","Jim Carrey","Meryl Streep","Joaquin Phoenix","Sam Rockwell","Leonardi DiCaprio","Heath Ledger","Robin Williams","Bill Murray","Will Ferrel","Jack Nicholson","Kevin spacey","Tom Cruise"]]},13:{family:["Tech Blogs"],types:[["TechCrunch","Wired","TechnoByte","Golem","Heise","Gizmodo","Engadget","Ars Technica","Techaeris","Top10Tech","10xDS","The Verge","TECHi","Urbangeekz","Techsppoks","Mashable"]]},14:{family:["PC Brands"],types:[["Asus","Intel","Dell","HP","Alienware","Microsoft","Apple","ACer","Sony","MSI","Razer","Toshiba","Gateway","LG","Compaq","Panasonic"]]},15:{family:["Evil Companies"],types:[["Facebook","Comcast","Time Warner","Google","Apple","Twitter","Go Daddy","Verizon","Yahoo","Microsoft","Zynga","BuzzFeed","McAfee","Amazon","Youtube","Hulu"]]}};
if (fixed) {
return famtypes
} else {
let familiesValNames =sheet.getRange(range).getValues();
let sRange
let obj = {};
for (i=0; i < 15; i++) {
sRange = 'A' + (4 + i) + ':P' + (4 + i)
Object.assign(obj, {[i +1]: {family: familiesValNames[i]}});
obj[i +1].types = sheet.getRange(sRange).getValues();
}
}
return JSON.stringify(obj);
}
function encoder (sheet, currentRow, colOffset, typeColNo, famSheet, famSheetTypesRange) {
let ret = {}
var str = ''
//loop through columns 3 to 18 of the corresponding row and iterate if its checked -> result binary string
for (i=0; i < 16; i++) {
str = (sheet.getRange(currentRow, (i+colOffset)).isChecked()) ? str + '1' : str + '0';
}
//shift one byte to left for easier calculation
//result are the first 2 byte (0xnnnnFF) of 3 (0xFFFFFF)
let var_bin = ((+parseInt(str,2)) << 8).toString(2)
//format binary as 3 byte
var_bin = ("000000000000000000000000".substr(var_bin.length) + var_bin);
//convert to hex
ret.variant_hex = parseInt(var_bin, 2).toString(16)
//get selected family type
ret.family_str = sheet.getRange(currentRow, typeColNo).getValue()
//get number of this selected type and convert it to hex
ret.family_hex = (get_family(ret.family_str, famSheet, famSheetTypesRange).number >>> 0).toString(16).toUpperCase();
//calculate intermedia hex value; add a trailing '0' to move the family to the upper 4bits of the last byte
ret.joined_hex = (parseInt(ret.variant_hex, 16) + parseInt((ret.family_hex + '0'), 16)).toString(16)
//convert to binary - better to make CRC with binary (1111 1111) than with HEX (ff)
let joined_bin = (parseInt(ret.joined_hex, 16)).toString(2)
//get crc and convert it to hex
ret.crc_hex = crc4(joined_bin).toString(16).toUpperCase()
//final hex code
ret.final_hex = (parseInt(ret.joined_hex, 16) + parseInt(ret.crc_hex, 16)).toString(16).toLocaleUpperCase()
//prepare and encode into base64
/** #type {number[]} */
ret.finalByteArr = [];
ret.finalByteArr[0] = ((parseInt(ret.final_hex, 16) & (parseInt('ff0000', 16))) >>> 16)
ret.finalByteArr[1] = ((parseInt(ret.final_hex, 16) & (parseInt('00ff00', 16))) >>> 8)
ret.finalByteArr[2] = (parseInt(ret.final_hex, 16) & (parseInt('0000ff', 16)))
//ret.finalByteArr = hexToBytes(ret.final_hex)
ret.b64 = b64().encode(ret.finalByteArr)
return ret
}
function decoder(str) {
var ret = {}
//will find on the end of the string the code; trailing whitespaces will be ignored
const regEx = /^(?:.*)#(.{4})(?:\s*)$/
//check if string is valid (checksum will be checked later)
ret.strValid = regEx.test(str);
if (ret.strValid) {
let result = str.match(regEx)
//first capturing group
ret.b64 = result[1];
//convert to a byte array
ret.byteArray = b64().decode(ret.b64)
//convert to hex string
ret.hex = bytesToHex(ret.byteArray).toString(16).toUpperCase();
//convert the hex string to binary
ret.binary = hex2bin(ret.hex, true)
//get crc
ret.crc = (parseInt(ret.binary, 2) & parseInt('00000f', 16)).toString(16).toUpperCase();
//get last 2 bytes (mask the 1st out) and check if its suits to the checksum
let toCheck = (parseInt(ret.binary, 2) & parseInt('fffff0', 16)).toString(2);
ret.crcValid = (crc4(toCheck).toString(16).toUpperCase() == ret.crc) ? true: false;
//get only variant
ret.variant_bin = ((parseInt(ret.binary, 2) & parseInt('ffff00', 16)) >>> 8).toString(2);
//fill with zero's & make a mirror bit set for easier handling later -> [0] = lowest bit
let variant_bin_ext = (("0000000000000000".substr(ret.variant_bin.length)) + ret.variant_bin.toString())
ret.variant_bin_mirr = variant_bin_ext.split("").reverse().join("");
//convert to hex
ret.variant_hex = parseInt(ret.variant_bin, 2).toString(16).toUpperCase()
//get type - mask everythin irrelevant out and move 4 bits to right
ret.type = parseInt(((parseInt(ret.binary, 2) & parseInt('0000f0', 16)) >>> 4), 10)
}
else {
ret.strValid = false;
}
return ret
}
function onEdit(event){
var sheet = event.source.getActiveSheet();
var actualSheetName = sheet.getName();
var lastColumnRow = sheet.getLastColumn();
var editRange = sheet.getActiveRange();
var editCol = editRange.getColumn();
var editRow = editRange.getRow();
var value = sheet.getActiveCell().getValue();
var adress = sheet.getActiveCell().getA1Notation();
var familiesSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Families');
//evaluate if something has been changed in 'Familees'
if (actualSheetName == 'Families') {
let all_types = set_family_object(familiesSheet, 'Q4:Q18', true)
Logger.log(all_types)
Logger.log(all_types[1].family)
Logger.log(all_types[1].types)
Logger.log(all_types[15].family)
Logger.log(all_types[15].types)
}
//evaluate and fill out legend on the top of sheet
if (adress == 'S2' && actualSheetName == 'Spares') {
const str = sheet.getActiveCell().getValue();
var famObj = get_family(str, familiesSheet, 'Q4:Q18')
sheet.getRange('C2:R2').setValues(famObj.names)
}
//getRange(row, column, [numRows], [numColumns]) || getRange(a1Notation)
if (sheet.getActiveCell().getRow() >= 6 && sheet.getActiveCell().getRow() <= 21
&& sheet.getActiveCell().getColumn() >= 3 && sheet.getActiveCell().getColumn() <= 19
&& actualSheetName == 'Spares') {
let currentRow = sheet.getActiveCell().getRow();
let currentCol = sheet.getActiveCell().getColumn();
//calculate code
let codeEnc = encoder(sheet, currentRow, 3, 19, familiesSheet, 'Q4:Q18')
if (sheet.getRange(currentRow, 1).getValue().length > 3) {
sheet.getRange(currentRow, 20).setValue(codeEnc.crc_hex);
sheet.getRange(currentRow, 20).setComment('')
sheet.getRange(currentRow, 21).setValue(codeEnc.b64);
sheet.getRange(currentRow, 21).setComment('')
} else {
sheet.getRange(currentRow, 20).setValue('n/a');
sheet.getRange(currentRow, 20).setComment('Please enter a product name in the 1st column.')
sheet.getRange(currentRow, 21).setValue('n/a');
sheet.getRange(currentRow, 21).setComment('Please enter a product name in the 1st column.')
}
let decoded = decoder('grgrgr #' + codeEnc.b64)
Logger.log(codeEnc)
Logger.log(decoded)
var famObj2 = get_family(decoded.type, familiesSheet, 'Q4:Q18')
Logger.log(famObj2)
}
The result can be tried/decoded with following HTML/JS snippet. Its quick and dirty.
Just enter for example:
Pneumatic Connector #Xs2a
Camera #!$cg
Power Supply 24V #p%qz
You can try to change manually the base64 code - for testing if the CRC4 works.
var crc4 = function(data) {
//https://gist.github.com/bryc/5916452ad0d1ef5c39f1a3f19566d315
var POLY = 0xC, INIT = 0, XOROUT = 0;
for(var crc = INIT, i = 0; i < data.length; i++) {
crc = crc ^ data[i];
for(var j = 0; j < 8; j++) {
crc = crc & 1 ? crc >>> 1 ^ POLY : crc >>> 1;
}
}
return (crc ^ XOROUT) & 0xF;
};
// Convert a hex string to a byte array
function hexToBytes(hex) {
// https://stackoverflow.com/a/34356351/14226613
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
// https://stackoverflow.com/a/34356351/14226613
for (var hex = [], i = 0; i < bytes.length; i++) {
var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
hex.push((current >>> 4).toString(16));
hex.push((current & 0xF).toString(16));
}
return hex.join("");
}
function hex2bin(hex, fill = false){
//https://stackoverflow.com/a/45054052/14226613
let bin = (parseInt(hex, 16).toString(2)).padStart(8, '0');
if (fill) {
//https://stackoverflow.com/a/27641835/14226613
bin = "000000000000000000000000".substr(bin.length) + bin;
}
return bin
}
function b64(){
const abc = "ABCDEFGH!JKLMN=PQRSTUVWXYZabcdefghijk:mnopqrstuvwxyz+-$%23456789"; // base64 alphabet
function encode (byteArray) {
//https://stackoverflow.com/a/62365404/14226613
const bin = n => n.toString(2).padStart(8,0); // convert num to 8-bit binary string
const l = byteArray.length
let result = '';
for(let i=0; i<=(l-1)/3; i++) {
let c1 = i*3+1>=l; // case when "=" is on end
let c2 = i*3+2>=l; // case when "=" is on end
let chunk = bin(byteArray[3*i]) + bin(c1? 0:byteArray[3*i+1]) + bin(c2? 0:byteArray[3*i+2]);
let r = chunk.match(/.{1,6}/g).map((x,j)=> j==3&&c2 ? '=' :(j==2&&c1 ? '=':abc[+('0b'+x)]));
result += r.join('');
}
//remove padding - http://jsfiddle.net/bicycle/c49fgz8x/
result = result.replace(/={1,2}$/, '');
return result;
}
function decode (str) {
//https://stackoverflow.com/a/62364519/14226613
let result = [];
//add padding http://jsfiddle.net/bicycle/c49fgz8x/
str = str + Array((4 - str.length % 4) % 4 + 1).join('=');
for(let i=0; i<str.length/4; i++) {
let chunk = [...str.slice(4*i,4*i+4)]
let bin = chunk.map(x=> abc.indexOf(x).toString(2).padStart(6,0)).join('');
let bytes = bin.match(/.{1,8}/g).map(x=> +('0b'+x));
result.push(...bytes.slice(0,3 - (str[4*i+2]=="=") - (str[4*i+3]=="=")));
}
return result;
}
return {
encode: encode,
decode: decode
}
}
function set_family_object (sheet, range, fixed = false){
let famtypes={1:{family:["Spares ACME 1"],types:[["spare","Alpha v2","Alpha v1","Alpha v0","spare","Beta v2","Beta v1","Beta v0","spare","Gamma v2","Gamma v1","Gamma v0","spare","Delta v2","Delta v1","Delta v0"]]},2:{family:["Spares ACME 2"],types:[["spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","Omega v2","Omega v2","Omega v1","Omega v0"]]},3:{family:["ACME Tools"],types:[["p612 lcp2","p612 lcp1","p612 lcp0","p512 lcp2","p512 lcp1","p512 lcp0","p671 lcp2","p671 lcp1","p671 lcp0","p471 lcp2","p471 lcp1","p471 lcp0","blm v2","blm v1","blm v0","common"]]},4:{family:["Animals"],types:[["Sea Otter","Cat","Panda","Fox","Dog","Lemur","Penguin","Mulgara","Degu","Kiwi","Koala","Monkey","Whale","Oribi","Chimpanzee","Deer"]]},5:{family:["Male Names"],types:[["James","John","Robert","Michael","Willian","David","Richard","Thomas","Charles","Daniel","Matthew","Donald","Mark","Brian","Edward","George"]]},6:{family:["Female Names"],types:[["Olivia","Emma","Ava","Sophia","Isabella","Charlotte","Amelia","Mia","Harper","Evelyn","Emily","Ella","Camila","Luna","Mila","Aria"]]},7:{family:["Car Brands"],types:[["Skoda","Renault","Citroen","Dacia","BMW","Audi","VW","Ford","Peugeot","Honda","Hyundai","Toyota","Kia","Opel","Porsche","Tata"]]},8:{family:["Horror Movies"],types:[["Wrong Turn","Descent","Hostel","Vacancy","Joy ride","Joy Ride","Midsommar","Cave","Eden Lake","Frozen","Ruins","Turistas","Hills have Eyes","Cabin of Woods","Soon the Darkness","Deep"]]},9:{family:["Things for Salad"],types:[["Lettuce","Cucumber","Cheese","Carrot","Tomato","Spinach","Crouton","Bacon","Egg","Onion","Chicken","Avocado","Pepper","Cabbage","Olive Oil","Feta"]]},10:{family:["TV Scientist"],types:[["Spock","Gaius Baltar","Leonard McCoy","Dr Bruce Banner","MacGyver","Dr Krieger","Walter White","Dexter","Lex Luthor","Walter Bishop","Data","The Doctor","Dr Quest","Dana Scully","Jimmy Neutron","Howard Wolowitz"]]},11:{family:["Funny Movies"],types:[["Spaceballs","Airplane!","Monty Python","Blazin Saddles","Lego Movie 2","Shrek","Naked gun","Austin Powers","Shaun of the Dead","Finding Nemo","The Big Lebowski","Dumb and Dumber","Simpsons","Toy Story","South Park","Life of Brian"]]},12:{family:["Actors"],types:[["Jamie Foxx","Val Kilmer","Christian Bale","Cate Blanchett","Jim Carrey","Meryl Streep","Joaquin Phoenix","Sam Rockwell","Leonardi DiCaprio","Heath Ledger","Robin Williams","Bill Murray","Will Ferrel","Jack Nicholson","Kevin spacey","Tom Cruise"]]},13:{family:["Tech Blogs"],types:[["TechCrunch","Wired","TechnoByte","Golem","Heise","Gizmodo","Engadget","Ars Technica","Techaeris","Top10Tech","10xDS","The Verge","TECHi","Urbangeekz","Techsppoks","Mashable"]]},14:{family:["PC Brands"],types:[["Asus","Intel","Dell","HP","Alienware","Microsoft","Apple","ACer","Sony","MSI","Razer","Toshiba","Gateway","LG","Compaq","Panasonic"]]},15:{family:["Evil Companies"],types:[["Facebook","Comcast","Time Warner","Google","Apple","Twitter","Go Daddy","Verizon","Yahoo","Microsoft","Zynga","BuzzFeed","McAfee","Amazon","Youtube","Hulu"]]}};
if (fixed) {
return famtypes
} else {
let familiesValNames =sheet.getRange(range).getValues();
let sRange
let obj = {};
for (i=0; i < 15; i++) {
sRange = 'A' + (4 + i) + ':P' + (4 + i)
Object.assign(obj, {[i +1]: {family: familiesValNames[i]}});
obj[i +1].types = sheet.getRange(sRange).getValues();
}
}
return JSON.stringify(obj);
}
function decoder(str) {
var ret = {}
//will find on the end of the string the code; trailing whitespaces will be ignored
const regEx = /^(?:.*)#(.{4})(?:\s*)$/
//check if string is valid (checksum will be checked later)
ret.strValid = regEx.test(str);
if (ret.strValid) {
let result = str.match(regEx)
//first capturing group
ret.b64 = result[1];
//convert to a byte array
ret.byteArray = b64().decode(ret.b64)
//convert to hex string
ret.hex = bytesToHex(ret.byteArray).toString(16).toUpperCase();
//convert the hex string to binary
ret.binary = hex2bin(ret.hex, true)
//get crc
ret.crc = (parseInt(ret.binary, 2) & parseInt('00000f', 16)).toString(16).toUpperCase();
//get last 2 bytes (mask the 1st out) and check if its suits to the checksum
let toCheck = (parseInt(ret.binary, 2) & parseInt('fffff0', 16)).toString(2);
ret.crcValid = (crc4(toCheck).toString(16).toUpperCase() == ret.crc) ? true: false;
//get only variant
ret.variant_bin = ((parseInt(ret.binary, 2) & parseInt('ffff00', 16)) >>> 8).toString(2);
//fill with zero's & make a mirror bit set for easier handling later -> [0] = lowest bit
let variant_bin_ext = (("0000000000000000".substr(ret.variant_bin.length)) + ret.variant_bin.toString())
ret.variant_bin_mirr = variant_bin_ext.split("").reverse().join("");
//convert to hex
ret.variant_hex = parseInt(ret.variant_bin, 2).toString(16).toUpperCase()
//get type - mask everythin irrelevant out and move 4 bits to right
ret.type = parseInt(((parseInt(ret.binary, 2) & parseInt('0000f0', 16)) >>> 4), 10)
}
else {
ret.strValid = false;
}
return ret
}
document.getElementById('ssearch').onclick = function() {
let ttable = document.querySelector("body > table")
ttable.style.visibility = "visible";
let sval = document.getElementsByClassName('searchTerm')[0].value;
let dec_val = decoder(sval);
let famValues = set_family_object(null, null, true)
//console.log(dec_val)
//console.log(famValues)
var tbody = document.querySelector('tbody');
tbody.innerHTML = '';
if (!dec_val.strValid) {
tbody.innerHTML += '<tr style="background-color:red;color:white;"><td>' + 'String Validation' + '</td><td>Input String is not valid!</td></tr>'
} else {
if (dec_val.crcValid) {
tbody.innerHTML += '<tr style="background-color:#93C47D; font-size: 75%;"><td>' + 'String & CRC' + '</td><td>String & CRC valid</td></tr>'
tbody.innerHTML += '<tr style="background-color:#3C6478; color:white"><td>' + 'Family' + '</td><td>' + famValues[dec_val.type]['family'] + '</td></tr>'
for (var i = 0; i < 16; i++) {
tbody.innerHTML += '<tr><td>' +
[
((dec_val.variant_bin_mirr[i] == 1) ?
famValues[dec_val.type]['types'][0][15-i] :
'<s>' + famValues[dec_val.type]['types'][0][15-i] + '</s>'),
((dec_val.variant_bin_mirr[i] == 1) ?
'<span style="background-color:#93C47D; color:white">Compatible</span>' :
'<span style="background-color:red; color:white">NOT Compatible</span>')
].join('</td><td>') + '</td></tr>'
}
} else{
tbody.innerHTML += '<tr style="background-color:red;color: white;"><td>' + 'CRC' + '</td><td>Product Name is wrong - CRC invalid</td></tr>'
}
}
}
th, td {
border: 1px solid black;
}
table {
margin: auto;
width: 40%;
visibility: hidden;
}
#import url(https://fonts.googleapis.com/css?family=Open+Sans);
body{
background: #f2f2f2;
font-family: 'Open Sans', sans-serif;
}
.search {
width: 100%;
position: relative;
display: flex;
}
.searchTerm {
width: 100%;
border: 3px solid #00B4CC;
border-right: none;
padding: 5px;
height: 20px;
border-radius: 5px 0 0 5px;
outline: none;
color: #9DBFAF;
}
.searchTerm:focus{
color: #00B4CC;
}
.searchButton {
width: 100px;
height: 36px;
border: 1px solid #00B4CC;
background: #00B4CC;
text-align: center;
color: #fff;
border-radius: 0 5px 5px 0;
cursor: pointer;
font-size: 20px;
}
/*Resize the wrap to see the search bar change!*/
.wrap{
width: 50%;
position: absolute;
top: 10%;
left: 50%;
transform: translate(-50%, -50%);
}
<!-- https://codepen.io/huange/pen/rbqsD -->
<!-- https://uicookies.com/html-search-box/ -->
<div class="wrap">
<div class="search">
<input type="text" class="searchTerm" placeholder="Give me your product name...">
<button type="submit" class="searchButton" id="ssearch">
Check
</button>
</div>
</div>
<br><br><br><br><br><br><br><br>
<table>
<thead>
<th style="width:40%;text-align:left;">Name</th>
<th style="text-align:left;">Value</th>
</thead>
<tbody>
</tbody>
</table>
function columnToLetter(col) {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
return sh.getRange(1,col).getA1Notation().slice(0,-1);
}
Since your going to use it several times make an object with all of the columns you need as properties and it will make a very fast converter for you with no function calls and any of the context switching times.

How can i validate an elements existence by typing it into an input text field?

I have different id's on different elements that are used in an input field when the user can type in the letter (Element 1 = A, Element 2 = B.. Element 27 = AA). How can I properly scan these on input in order to determine if the element exists and if it does put it in a string that converts these id's to values which are later calculated?
I have an id system on a calculator where the user can generate different element (sliders, radio buttons, check boxes) that can be calculated. They all have a numeric id which is the translated into alphabetic characters in a progressive order ( Element 1 = A, Element 2 = B.. Element 27 = AA). I later have an input field where the user can create their own formula that will be calculated and put into a result tab.
Sample Formula: A+B*2
The reason I translate to letter is so that the user can use numbers in creating the formula. I have succeed in making this work for id's that are one letter but as soon as the id's start hitting AA and AB it doesn't work because currently I split this into an array and scan every element for it's value, which becomes problematic for two letters since they split into two different id's.
I have tried splitting the array based on the operators (+, -, *, /) but that removes them from the array.
function resultCalcInit(resultObject, resultFormulaObject) {
$('.createWrap').on('keyup input change', $(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula'), function(e) {
var thisKey = new RegExp(String.fromCharCode(e.which).toLowerCase());
var keyNoRegEx = String.fromCharCode(e.which).toLowerCase();
var counter = 0;
var letters = /^[0-9a-zA-Z]+$/;
for (var call of $('.dropzone').find('.builder-elements')) {
if ($(call).find('.bf-number')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.bf-number').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builder-list')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builder-list').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builder-radio')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builder-radio').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builderSlider')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builderSlider').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builder-checkboxes')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builder-checkboxes').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
}
}
if (String.fromCharCode(e.which).match(letters) && counter < 1) {
$(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').html($(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').html().replace(thisKey, ""));
var returnString = $(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').text();
$('#jvformbuilder-formula-panel').find('.jvformbuilder-formula-panel-elements').each(function() {
var formulaResultId = $(this).find('.jvformbuilder-formula-panel-elements-result-field-formula');
$('.builder-elements').each(function() {
if (formulaResultId.attr("id") == $(this).find('.result-number').attr("id")) {
var resultWindow = $(this).find('.result-number');
var formula = returnString.slice(1);
}
});
});
} else {
resultCalc(resultFormulaObject);
}
});
}
Here it check if the letter typed is an existing ID. If it isn't, it's removed. If it is, it stays and proceeds to be scanned for the value.
function resultCalc(resultFormulaObject) {
var returnString = $(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').text();
$('#jvformbuilder-formula-panel').find('.jvformbuilder-formula-panel-elements').each(function() {
var formulaResultId = $(this).find('.jvformbuilder-formula-panel-elements-result-field-formula');
$('.builder-elements').each(function() {
if (formulaResultId.attr("id") == $(this).find('.result-number').attr("id")) {
var resultWindow = $(this).find('.result-number');
var formula = returnString.slice(1).split("");
var formulaNbr = returnString.slice(1).split("");
var alphabet = ("abcdefghijklmnopqrstuvwxyz").split("");
var calculationArray = returnString.slice(1).split("");
var tempArr = formula;
for (var i = 0; i < formula.length; i++) {
$('.builder-elements').each(function() {
if ($(this).find('.builder-list').attr("data-calcid") == formula[i]) {
formulaNbr[i] = $(this).find('.builder-list').children("option:selected").val();
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
} else if ($(this).find('.builder-field').attr("data-calcid") == formula[i]) {
if ($(this).find('.bf-text')[0]) {
console.log(tempArr);
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.bf-telNum')[0]) {
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.bf-date')[0]) {
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.bf-number')[0]) {
if (!$(this).find('.bf-number').val()) {
formulaNbr[i] = 0;
} else {
formulaNbr[i] = $(this).find('.bf-number').val();
}
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
}
} else if ($(this).find('.builder-textarea').attr("data-calcid") == formula[i]) {
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.builder-radio').attr("data-calcid") == formula[i]) {
var resultRadio = [];
$(this).find('.builder-radio-input').each(function(i) {
resultRadio[i] = parseInt($(this).val());
});
var sum = resultRadio.reduce(add);
formulaNbr[i] = sum;
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
} else if ($(this).find('.builder-checkboxes').attr("data-calcid") == formula[i]) {
var resultCheck = [];
$(this).find('.builderCB').each(function(i) {
resultCheck[i] = parseInt($(this).val());
});
var sum = resultCheck.reduce(add);
formulaNbr[i] = sum;
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
} else if ($(this).find('.builderSlider').attr("data-calcid") == formula[i]) {
formulaNbr[i] = $(this).find('.builder-slider').val();
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
}
});
}
var calculationString = "";
for (var i = 0; i < calculationArray.length; i++) {
calculationString += calculationArray[i];
}
returnString = "";
for (var i = 0; i < formulaNbr.length; i++) {
returnString += formulaNbr[i];
}
if (returnString) {
printRes(returnString, resultWindow, calculationString);
}
}
});
});
}
Here it takes different values from the different objects that relates to the id written inside the formula tab. Later it is printed into the result tab.
function printRes(resString, resArea, calcString) {
resArea.empty();
var result = eval(resString);
if (!result) {
resArea.append(0)
resArea.attr("data-calcForm", "");
} else {
resArea.append(result)
resArea.attr("data-calcForm", calcString);
}
}
It completely crashes if the id becomes doubled. That's where I need you guys to help me. How can I make it scan after double characters id's as well as single ones, and triple ones and how ever many the user decides to generate.
There is no way to reproduce the issue you have from the big code bunch you posted. And I have to admit that what you try to achieve is unclear to me.
But what is really clear is that you need to segregate the formula's elements from the operator. You can achieve this with 2 regexes. I see you already use the first one: /[0-9a-zA-Z]+/, but I don't get how you use it...
Anyway, here is a real simple demo showing that you can have two arrays, one for the formula's elements and the other for the operators. Once you have that, you should be able to use it to do whatever you wish to.
$("button").on("click",function(){
// The input value
var input_val = $("input").val();
// Regexes
var elements_regex = /[0-9a-zA-Z]+/g;
var operators_regex = /[\+\-*\/]/g;
// Create the arrays
var elements_array = input_val.match(elements_regex);
var operators_array = input_val.match(operators_regex);
// Needed just for this demo
var regex_validation = $(".regex_validation");
var elements = $(".elements");
var operators = $(".operators");
// RESULTS
// Regex validation
for(i=0;i<elements_array.length;i++){
// Element
if(typeof(elements_array[i])!="undefined"){
regex_validation.append("<span class='element'>"+elements_array[i]+"<span>");
}
// Operator
if(typeof(operators_array[i])!="undefined"){
regex_validation.append("<span class='operator'>"+operators_array[i]+"<span>");
}
}
// Elements
elements.html(JSON.stringify(elements_array));
// Operators
operators.html(JSON.stringify(operators_array));
});
.result{
height: 50px;
width: 500px;
border: 1px solid black;
}
.element{
background: cyan;
}
.operator{
background: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Enter your formula: <input value="AA+B*2"><button type="button">Check</button><br>
<br>
Regex validation: (cyan for elements, yellow for operators)
<div class="result regex_validation"></div>
<br>
Elements array:
<div class="result elements"></div>
<br>
Operators array:
<div class="result operators"></div>
<br>
CodePen

js Page Freezing on from .length?

This function is freezing my page.
function findMode (array)
{
var modeArr = [];
var modeCounter = [];
modeArr.length = array.length;
modeCounter.length = array.length;
}
However, when I remove this it runs just fine.
modeArr.length = array.length;
modeCounter.length = array.length;
Here is all of my code:
<html>
<head>
</head>
<body>
<p> Please enter a series of numbers, each separated by a new line.<br><p>
<textarea id="myTextArea" rows = "7" cols = "50"></textarea><br>
<button onclick="processData()">Done</button>
<p id = "mean"></p>
<p id = "median"></p>
<p id = "count"></p>
<p id = "summation"></p>
<p id = "mode"></p>
<p id = "variance"></p>
<p id = "sd"></p>
<script type = "text/javascript">
var mean = 0;
var median = 0;
var count = length;
var mode = 0;
var variance = 0;
var standard_deviation = 0;
var meanOutput = document.getElementById('mean');
var medianOutput = document.getElementById('median');
var modeOutput = document.getElementById('mode');
var countOutput = document.getElementById('count');
var summationOutput = document.getElementById('summation');
var varianceOutput = document.getElementById('variance');
var sdOutput = document.getElementById('sd');
function processData()
{
var arrayOfLines = document.getElementById('myTextArea').value.split('\n');
var sum = findSum(arrayOfLines);
findMean(arrayOfLines, sum);
findMedian(arrayOfLines);
findMode(arrayOfLines);
findVariance(arrayOfLines);
findStandardDeviation(arrayOfLines);
findVariance(arrayOfLines);
}
function findSum (array)
{
var count = array.length;
var sum = 0;
for (var a = 0; a < array.length; a++)
{
sum += parseInt(array[a]);
}
countOutput.innerHTML = "Count: " + array.length;
summationOutput.innerHTML = "Sum: " + JSON.stringify(sum);
return sum;
}
function findMode (array)
{
var modeArr = [];
var modeCounter = [];
modeArr.length = array.length;
modeCounter.length = array.length;
for (var a = 0; a < array.length; a++)
{
for (var b = 0; b < modeArr.length; b++)
{
if (modeArr[a] == modeArr[b])
{
modeCounter[a]++;
}
if (a == 0)
{
b--;
}
}
modeArr[a] = array[a];
}
modeOutput.innerHTML = "Mode: ";
}
function findMean (array, sum)
{
mean = sum/array.length;
meanOutput.innerHTML = "Mean: " + mean.toPrecision(2);
}
function findMedian (array)
{
for(var i=0; i < array.length; i++)
{
array[i] = +array[i];
}
var sortedArrayOfLines = array.sort(function(a, b){return a - b});
if (array.length % 2 == 1)
{
median = sortedArrayOfLines[((array.length - 1)/2)]
}
else
{
median = (sortedArrayOfLines[array.length/2] + sortedArrayOfLines[(array.length/2)+1])/2
}
medianOutput.innerHTML = "Median: " + median;
}
function findVariance (array)
{
var mean = mean(array);
return mean(array.map(function(num)
{
varianceOutput.innerHTML = Math.pow(num - mean, 2);
}));
}
function findStandardDeviation (array)
{
medianOutput.innerHTML = Math.sqrt(variance(array));
}
</script>
</body>
</html>
So the issue isn't the length it's a infinite loop.
The problem is this bit of code
if (a == 0)
{
b--;
}
This is inside the following loop with b as the iterator. See below.
for (var b = 0; b < modeArr.length; b++)
a is set to zero by the outer loop. Thus a==0 is always true inside the inner loop. b will never increase only decrease. Thus this is a infinite loop because b will never be greater than modeArr.length.
So I would consider revising the function, below is a example of a possible candidate for a mode function:
Get the element with the highest occurrence in an array

Javascript: Calculating Min/Max/Average

I wrote this code in my html site, in Javascript, but is not working right. Most times it seems to ignore some entries and just randomly selects which is the min/max value. Also, when I tried to calculate average values, I got a string instead of a number, even though the variable is declared as 0 in the beginning. e.g performing 0+1+1+2+3+5 = 011235 instead of 12.
Here is the code, thanks in advance.
**EDIT: I added the student average code in the end, but it doesn't work, it doesn't show any results on the page, not even the "student" + [i] part. On the other hand, the parseInt() command worked, and made everything work as it should, thank you :)
<script language = "javascript">
function myFunction() {
var course0 = [];
var course1 = [];
var course2 = [];
var minstugrade = 100;
var maxstugrade = 0;
var minstugradetext = "";
var maxstugradetext = "";
var stuavgarr = [];
var minstuavg = 100;
var maxstuavg = 0;
var minstuavgtext = "";
var maxstuavgtext = "";
var mincougrade = 100;
var maxcougrade = 0;
var mincougradetext = "";
var maxcougradetext = "";
var mincouavg = 100;
var maxcouavg = 0;
var mincouavgtext = "";
var maxcouavgtext = "";
var couavg = 0;
//add form items to array
var x = document.getElementById("course0");
var i;
for (i = 0; i < x.length ;i++) {
course0.push(parseInt(x.elements[i].value));
}
var x = document.getElementById("course1");
var i;
for (i = 0; i < x.length ;i++) {
course1.push(parseInt(x.elements[i].value));
}
var x = document.getElementById("course2");
var i;
for (i = 0; i < x.length ;i++) {
course2.push(parseInt(x.elements[i].value));
}
//calculate course & student min/max
for (i = 0; i < course0.length; i++) {
if (course0[i] < mincougrade) {
mincougrade = course0[i];
mincougradetext = "course0";
}
if (course0[i] > maxcougrade) {
maxcougrade = course0[i];
maxcougradetext = "course0";
}
if (course0[i] < minstugrade) {
minstugrade = course0[i];
minstugradetext = "student" + [i];
}
if (course0[i] > maxstugrade) {
maxstugrade = course0[i];
maxstugradetext = "student" + [i];
}
}
for (i = 0; i < course1.length; i++) {
if (course1[i] < mincougrade) {
mincougrade = course1[i];
mincougradetext = "course1";
}
if (course1[i] > maxcougrade) {
maxcougrade = course1[i];
maxcougradetext = "course1";
}
if (course1[i] < minstugrade) {
minstugrade = course1[i];
minstugradetext = "student" + [i];
}
if (course1[i] > maxstugrade) {
maxstugrade = course1[i];
maxstugradetext = "student" + [i];
}
}
for (i = 0; i < course2.length; i++) {
if (course2[i] < mincougrade) {
mincougrade = course2[i];
mincougradetext = "course2";
}
if (course2[i] > maxcougrade) {
maxcougrade = course2[i];
maxcougradetext = "course2";
}
if (course2[i] < minstugrade) {
minstugrade = course2[i];
minstugradetext = "student" + [i];
}
if (course2[i] > maxstugrade) {
maxstugrade = course2[i];
maxstugradetext = "student" + [i];
}
}
//calculate course average
for (i = 0; i < course0.length; i++) {
couavg += course0[i];
}
couavg = couavg / course0.length
if (couavg < mincouavg) {
mincouavg = couavg;
mincouavgtext = "course0";
}
if (couavg > maxcouavg) {
maxcouavg = couavg;
maxcouavgtext = "course0";
}
couavg = 0;
for (i = 0; i < course1.length; i++) {
couavg += course1[i];
}
couavg = couavg / course1.length
if (couavg < mincouavg) {
mincouavg = couavg;
mincouavgtext = "course1";
}
if (couavg > maxcouavg) {
maxcouavg = couavg;
maxcouavgtext = "course1";
}
couavg = 0;
for (i = 0; i < course2.length; i++) {
couavg += course2[i];
}
couavg = couavg / course2.length
if (couavg < mincouavg) {
mincouavg = couavg;
mincouavgtext = "course2";
}
if (couavg > maxcouavg) {
maxcouavg = couavg;
maxcouavgtext = "course2";
}
//calculate student average
for (i = 0; i < course0.length; i++) {
stuavgarr[i] += course0[i];
stuavgarr[i] += course1[i];
stuavgarr[i] += course2[i];
}
for (i=0; i < stuavgarr.length; i++) {
stuavgarr[i] = stuavgarr[i] / course0.length;
if (stuavgarr[i] < minstuavg) {
minstuavg = stuavgarr[i];
minstuavgtext = "student" + [i];
}
if (stuavgarr[i] > maxstuavg) {
maxstuavg = stuavgarr[i];
maxstuavgtext = "student" + [i];
}
}
document.getElementById("studmaxgrade").innerHTML = "Student that achieved the max grade is " + maxstugradetext
document.getElementById("studmingrade").innerHTML = "Student that achieved the min grade is " + minstugradetext
document.getElementById("studmaxavg").innerHTML = "Student that achieved the max average is " + maxstuavgtext
document.getElementById("studminavg").innerHTML = "Student that achieved the min average is " + minstuavgtext
document.getElementById("courmaxgrade").innerHTML = "The course in which the max grade is scored is " + maxcougradetext
document.getElementById("courmingrade").innerHTML = "The course in which the min grade is scored is " + mincougradetext
document.getElementById("courmaxavg").innerHTML = "The course in which the max average grade is scored is " + maxcouavgtext
document.getElementById("courminavg").innerHTML = "The course in which the min average grade is scored is " + mincouavgtext
}
</script>
The value of an input is a string, thus a + b will be interpreted as appending one string to another.
If you make sure the first parameter (a in this case) is an integer a + b will result in the two being mathematically adding the two
console.log( '0' + 1 + 2 + 3 + 4 ); //* outputs 01234
console.log( parseInt( 0 ) + 1 + 2 + 3 + 4 ); //* outputs 10
JSFiddle
Ok for a start you seem very confused about
document.getElementById
This does not address a javascript variable at all......
This literally "gets the document element by its id".
Here is an example of how to use it...
<html>
<img id='my_new_selfie' src='me.jpg'>
....
....
<script>
alert (document.getElementById('my_new_selfie').src)
</script>
This would simply pop up an alert with the text that describes the src of the
document object who's id is 'my_new_selfie'
that is....
[me.txt]
The reason that document.getElementById was introduced to javascript was to save developers learning the DOM (document object model) in order to access objects.
It allows you to simply give you object an id and change things about it using the id
In the above example I could use a script or button to change the image source
an example of this might be using the onclick event of another object on the page like a button...
onclick='document.getElementById('my_new_selfie').src='new_pic_of_me.JPG'
It is not used to identify variables in a javascript

making groups with random names in it in javascript

I am new to coding Javascript. I am trying to to shuffle list of names inputted on a textarea. The user selects the number of groups desired, and shuffle on click, then show the divided groups as output result. Below is my code but it is not working as it should be, pls help!
<script>
function ArrayToGroups(source, groups){
var groupList = [];
groupSize = Math.ceil(source.length/groups);
var queue = source;
for(var r = 0; r < groups; r++){
groupList.push(queue.splice(0,groupSize));
}
return groupList;
}
function textSpliter(splitText){
var textInput = document.getElementById("inputText").value;
var splitText = textInput.split(',');
var newList = [];
for(x = 0; x <= splitText.length; x++) {
var random = Math.floor(Math.random() * splitText.length);
var p = splitText[random];
newList.push(p);
splitText.splice(p,groupList);
}
for(var i = 0; i < newList.length; i++){
var s = newList[i];
document.getElementById('resInput').value += s + "\n" ;
}
return splitText;
}
</script>
Below is my input and output textareas
</head>
<body>
<form>
<textarea id="inputText" placeholder="text" rows="10" cols="40"></textarea>
<input type="number" name="number" max="6" value="1" id="groupNumber">
<textarea id="resInput" placeholder="text" rows="10" cols="40"></textarea>
<input type="button" name="Shuffle" value="shuffle" onclick="textSpliter()">
</form>
</body>
</html>
function shuffle() {
// Get list
// Example: element1, element 2, ele ment 3, ...
var list = document.getElementById("inputText").value.replace(/\s*,\s*/g, ",").split(",");
// Get number of groups
var n = parseInt(document.getElementById("groupNumber").value);
// Calculate number of elements per group
var m = Math.floor(list.length / n);
// Enought elements
if (n * m == list.length) {
// Create groups
var groups = new Array();
for (i = 0; i < n; i++) {
groups[i] = new Array();
for (j = 0; j < m; j++) {
// Random
rand = Math.floor(Math.random() * list.length);
// Add element to group
groups[i][j] = list[rand];
// Remove element to list
list.splice(rand, 1);
}
}
// Output
var text = "";
for (i = 0; i < n; i++) {
text += "Group " + (i + 1) + ": ";
for (j = 0; j < m; j++) {
if (j != 0) { text += ", "; }
text += groups[i][j];
}
text += "\n";
}
document.getElementById("resInput").value = text;
} else {
alert("Add more elements");
}
}
I rewrote your code. It's pretty self-explanatory.
FIDDLE
function textSpliter() {
var input = document.getElementById("inputText").value;
var names = input.split(",");
var groupSize = document.getElementById("groupNumber").value;
var groupCount = Math.ceil(names.length / groupSize);
var groups = [];
for (var i = 0; i < groupCount; i++) {
var group = [];
for (var j = 0; j < groupSize; j++) {
var random = Math.floor(Math.random() * names.length);
var name = names[random];
if (name != undefined) {
group.push(name);
names.splice(names.indexOf(name), 1);
}
}
group.sort();
groups.push(group);
}
printGroups(groups);
}
function printGroups(group) {
var output = document.getElementById("resInput");
output.value = "";
for (var i = 0; i < group.length; i++) {
var currentGroup = "";
for (var j = 0; j < group[i].length; j++) {
currentGroup = group[i].join(",");
}
output.value += currentGroup + "\r";
}
}
ES6 version ;-)
http://jsfiddle.net/dLgpny5z/1/
function textSpliter() {
var input = document.getElementById("inputText").value;
var names = input.replace(/\s*,\s*|\n/g, ",").split(",");
var groupSize = document.getElementById("groupNumber").value;
var groupCount = Math.ceil(names.length / groupSize);
var groups = [...Array(groupCount)].map(() => Array());
var i = 0
while (names.length > 0) {
var m = Math.floor(Math.random() * names.length);
groups[i].push(names[m]);
names.splice(m, 1);
i = (i >= groupCount - 1) ? 0 : i + 1
}
printGroups(groups);
}
function printGroups(groups) {
var output = document.getElementById("resInput");
output.value = groups.map(group => group.join(',')).join('\r');
}

Categories