Since two days I have a problem with arrays and conditions in JavaScript.
Could you check my code and give me suggestions what should I correct to run this code without infinite loop?
The list of steps (to clarify my problem) - how this code should work:
1. starting the draw
2. Get random quote -> quote[r_quote] where r_quote is random number
3. If the tmp array is empty, add drawn quote into tmp[j]
4. Get next random quote (step 2)
5. If the tmp is not empty
6. Compare all tmp elements with the new, random quote
7. If the new random quote is unique, add it to tmp[j]
I've currently problem with step 7. When this part of code is uncommented, the loop begins to be processed infinity.
var quote = new Array();
quote[0] = 'AAAAAAAAAA';
quote[1] = 'Lorem ipsum.';
quote[2] = 'BBBBBBBBBB';
quote[3] = 'CCCCCCCCCC';
var numberOfQoutes = quote.length;
var r_quote;
var tmp = new Array();
var j = 0;
console.log("start... ");
for (i = 0; i < 3; i++) {
r_quote = Math.floor(numberOfQoutes * Math.random());
console.log('[Step ' + i + '] \t\nquote[' + r_quote + ']' + ' => ' + quote[r_quote]);
if(tmp.length == 0) {
console.log('\ttmp array is empty. You can append ANY quote.');
tmp[j] = quote[r_quote];
console.log('\t\tAppended: ' + tmp[j]);
console.log(j); // show current tmp array position
j++;
} else {
// add just unique qoute
console.log('\ttmp array is not empty.');
console.log('\tChecking if drawn quotation has already been appended.')
for(k = 0; k < tmp.length; k++) {
console.log("\tk=" + k + "\t" + tmp[k] + "\t<< comparing >>\t" + quote[r_quote]);
if(tmp[k] !== quote[r_quote]) { // this if probably doesn't work
console.log("\t\t[OK] You can append quote.");
console.log(j); // show current tmp array position
console.log('\t\tAssign this quote[' + r_quote + '] => ' + quote[r_quote]);
console.log('\t\tInto tmp[' + j + ']');
/* what's wrong here?
tmp[j] = quote[r_quote]; // I want to add random qoute to new position in array
console.log("Appended: " + tmp[j]);
console.log(k);
j++;
*/
}
}
}
}
The problem is that here:
for(k = 0; k < tmp.length; k++) {
you check whether k < tmp.length, and here:
tmp[j] = quote[r_quote];
j++;
you always add another element to the array, so it's length keeps climbing until you run out of memory
Related
the following code is supposed to count the number of heads vs tails. The code below was given to me but I was tasked with counting heads vs tails, I tried with the function function countHeadsAndTails(flips) and below but running into a small issue. I have triple asterisked the line that is giving me an error: arr is not defined (under the function countHeadsAndTails(flips)) I'm sure this is just a silly mistake and I'd hate to waste your time with such an easy fix but I've been banging my head against the wall for the past 30 mins trying to solve it, thanks :)
var NUM_FLIPS = 100;
var headCount = 0, tailCount = 0;
function start(){
var flips = flipCoins();
printArray(flips);
}
// This function should flip a coin NUM_FLIPS
// times, and add the result to an array. We
// return the result to the caller.
function flipCoins(){
var flips = [];
for(var i = 0; i < NUM_FLIPS; i++){
if(Randomizer.nextBoolean()){
flips.push("Heads");
}else{
flips.push("Tails");
}
}
return flips;
}
function printArray(arr){
for(var i = 0; i < arr.length; i++){
println(i + ": " + arr[i]);
}
countHeadsAndTails();
}
function countHeadsAndTails(flips) {
for (var i = 0; i < NUM_FLIPS; i++) {
***if (arr["flips"] === "heads")***
headCount += arr[i];
else
tailCount += arr[i];
}
print("Heads: " + headCount + " " + "Tails: " + tailCount);
}
You have not declared the arr array as global, therefore you have to pass it to functions that are supposed to use it. The arr array is actually flips. I changed your code below to pass the array to the countHeadsAndTails() function and also added a couple other small changes to the same function (see arrows below).
Run and test:
var NUM_FLIPS = 100;
var headCount = 0, tailCount = 0;
function start(){
var flips = flipCoins();
printArray(flips);
}
// This function should flip a coin NUM_FLIPS
// times, and add the result to an array. We
// return the result to the caller.
function flipCoins(){
var flips = [];
for(var i = 0; i < NUM_FLIPS; i++){
if( Math.round(Math.random()) ){ // <- To mimic Randomizer
flips.push("Heads");
} else {
flips.push("Tails");
}
}
return flips;
}
function printArray(arr){
for(var i = 0; i < arr.length; i++){
console.log(i + ": " + arr[i]);
}
countHeadsAndTails(arr); // <- passing array to function
}
function countHeadsAndTails(flips) { // <- array is now called flips again
for (var i = 0; i < NUM_FLIPS; i++) {
if (flips[i] === "Heads") // <- check the ith element, and capital H
headCount++; // <- increment headCount
else
tailCount++; // <- increment tailCount
}
console.log("Heads: " + headCount + " " + "Tails: " + tailCount);
}
start();
Note: I also changed print() and println() to console.log() in order to be consistent with correct JavaScript output syntax.
novice programmer here.
I'm currently working on a project that pumps integers into a text field, yet decided to run the array through a for loop which shoves each item into a string that's more presentable for the user. The for loop, however, is displaying this in the console.log:
2 + 1 + 4 + 1 +
Bit of clarification - it's a dice roller. The for loop segment of this click event is below, and I don't know how to specify to concatenate the last array item to not include a "+" but a "=" instead.
let diceString = '';
for (let i = 0; i <= d4Array.length; i++) {
if(i == d4Array.length) {
diceString += d4Array[i] + ' =';
break;
}
else {
diceString += d4Array[i] + ' + ';
}
console.log(diceString);
}
Your for loop iterates over 5 items in your old code. d4Array.length will be 4, but since the index starts at 0 it will iterate over 5 items before i reaches 4 ( 0,1,2,3,4 ). That is why your last item returns undefined. You can fix this by replacing let i = 0; i <= d4Array.length; i++ with let i = 0; i < d4Array.length; i++.
To select the last item i would have to be the length of the array minus one for the same reason. Thus turn i === d4Array.length into i === d4Array.length - 1
d4Array = [2,1,4,1];
let diceString = '';
for (let i = 0; i < d4Array.length; i++) {
if(i === d4Array.length - 1 )
diceString += d4Array[i] + ' =';
else
diceString += d4Array[i] + ' + ';
console.log(diceString);
}
So I have this for loop, I'm stuck in the second one, but If I am, I'd also be stuck in the first one, whats the problem here?
for(var i = 0; i < oldtds.length;i += 3)
{
var oldNameIndex = sameName(oldtds[i].innerHTML, nameList);
if(oldNameIndex != -1)
{
//This means the name already exists
//I need to combine the times, and remove the tr from the table for the second name
//this loop accesses all the times
for(var j = i + 1; j < oldtds.length; j += 3)
{
//This won't quite work, There are colons between them
var timerArray = oldtds[j].innerHTML.split(":");
timerArray.push(oldtds[oldNameIndex + 1].innerHTML.split(":"));
console.log(timerArray);
console.log("this is j " + j);
}
}
}
I am currently trying to create a double nested loop that adds a number to itself, given the number of instances you want it to be added by.
So when you input something in the Number, for example "5" and you input "3" for the number of instances, then the following would be printed:
5=5
5+5=10
5+5+5=15
More information on my JsFiddle
<div>
<h2>Loop</h2>
Number
<input type='text' id='tbox'>
<br>
Number of Instances
<input type='text' id='theNumber'>
<button onclick=doubleLoop;>
Add Numbers.
</button>
</div>
<div id="content">
</div>
<script>
function doubleLoop(){
var theText = document.getElementById('tbox').value;
var theNumber = document.getElementById('theNumber').value;
var content = document.getElementById('content');
content.innerHTML = '';
for (var i = 0; i < theNumber; i++) {
content.innerHTML = content.innerHTML + (i + 1) + ')';
//start of the second part of the Double Loop
for (var j = 0; j < (i + 1); j++){
if (i === 0){
content.innerHTML = content.innerHTML + theText + '=' + theText + '<br>';
} else if (i > 0) {
content.innerHTML = content.innerHTML + theText.repeat(j) + '=' + (theText * (i+1));
}
}
}
}
</script>
Here you go
https://jsfiddle.net/mkarajohn/qkn2ef4L/
function createString(number, times) {
/*
* We will create each side of the equation separately and we will concatenate them at the end
*/
var leftSide = '',
rightSide = '',
i;
for (i = 1; i <= times; i++) {
leftSide += number.toString();
if ((times > 1) && (i < times)) {
leftSide += '+';
}
}
rightSide = number * times
return (leftSide + '=' + rightSide);
}
function loop(){
// .value returns a string, so we make sure the values are converted to integers by calling parseInt()
// See https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/parseInt
var theText = parseInt(document.getElementById('tbox').value);
var theNumber = parseInt(document.getElementById('theNumber').value);
var content = document.getElementById('content');
var output = '';
content.innerHTML = '';
for (var i = 1; i <= theNumber; i++) {
output += createString(theText, i);
output += '<br />'
}
content.innerHTML = output;
}
var button = document.getElementById('run');
run.addEventListener('click', loop);
If there is something that is not clear feel free to ask.
EDIT: If you are hell bent on doing it with two nested loops, here's how it would go:
function loop(){
// .value returns a string, so we make sure the values are converted to integers by calling parseInt()
// See https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/parseInt
var theText = parseInt(document.getElementById('tbox').value);
var theNumber = parseInt(document.getElementById('theNumber').value);
var content = document.getElementById('content');
var output = '';
var leftSide = '',
rightSide = '';
content.innerHTML = '';
for (var i = 1; i <= theNumber; i++) {
leftSide = '';
for (var j = 1; j <= i; j++) {
leftSide += theText.toString();
if ((i > 1) && (j < i)) {
leftSide += '+';
}
}
rightSide = theText * i;
output += (leftSide + '=' + rightSide);
output += '<br />'
}
content.innerHTML = output;
}
var button = document.getElementById('run');
run.addEventListener('click', loop);
First things first: You're naming your variables very poorly, it's really difficult to understand what you're trying to do, specially when you don't say what you want directly in the question. doubleLoop says how your function works but not what it does. getMultiplicationProcess would have been a better name. Also, you could be passing the values as arguments and just returning the result, it would look A LOT better.
Anyway, I couldn't figure how you were trying to achieve this. I've renamed your variables and did everything my way. Never name a variable theNumber or theText because doing so says nothing about what information it holds. You could have named them firstInput and secondInput but even that way it would not be clear.
Here's the code, scroll down for explanation:
var submit = document.getElementById("submit"),
firstInput = document.getElementById("tbox"),
secondInput = document.getElementById("theNumber"),
answerField = document.getElementById("content");
submit.addEventListener("click", function () {
answerField.innerHTML = getMultiplicationProcess(Number(firstInput.value), Number(secondInput.value), "<br/>");
});
function getMultiplicationProcess(multiplicand, multiplier, lineBreak) {
var result = "";
for (var i = 0; i < multiplier; ++i) {
for (var j = 0; j < i + 1; ++j) {
if (i === j) {
result += multiplicand + " = " + (multiplicand * (i + 1));
} else result += multiplicand + " + ";
}
result += lineBreak || "\n";
}
return result;
}
JSFiddle
Explanation:
The outer for loop runs as many times as the second input, or multiplier. So if you input 5 and 3 respectively this loop will run three times. It represents each line of the resulting string.
The inner loop runs as many times as the current iteration number of the outer loop more one. So for our example inputs it will run like this:
0: 1; 1: 2; 2: 3;
I use it to place the multiplicand multiple times in the current line.
The first line will contain a single 5 (not including the answer for this multiplication) so j is i + 1 which is 1 because during the first iteration from the outer loop i equals 0:
5 = 5
The second line contains 2 5s and i is 1 because we're in the second iteration for the outer loop, so j = i + 1 = 2 which is how many fives we'll place in the string:
5 + 5 = 10
if it's the last iteration of the inner loop instead of adding "5 + " to the resulting string it places "5 = (i + 1) * multiplier" which will be the result for the current line. Then the inner loop ends, the outer loop adds a line break and restarts the process for the next line.
I need to fill a pdf form automatically in my angularjs webapp. The pdf form is generated outside the app so I can configure it as I want.
In my app, I just need to load the pdf, modify the form fields and flatten the file so it doesn't look like a form anymore.
Do you know any way to do it?
Edit:
I've found iText but it's a java library which won't work for my project (the app runs on tablets so I'm looking for something 100% HTML5)
There is now another solution that worked for me.
It's this JS package: https://github.com/phihag/pdfform.js
Also available on NPM: https://www.npmjs.com/package/pdfform.js
If you have troubles opening your PDF files, the best bet is to use the mozilla's PDF.js library which works with every PDF files I tried.
The only drawback as of today is that it's not working as expected with webpack. You have to import the JS files with a script tag.
I've found a solution...not perfect but it should fit most requirements. It doesn't use any server (perfect for privacy requirements) or library! First of all, the PDF must be version 1.5 (Acrobat 6.0 or later). The original pdf can be another version but when you create the fields you must save it as compatible for Acrobat 6.0 or later. If you want to make sure the format is right, you can check there
So let's say I have 'myform.pdf' file (with no form fields); I open it with Acrobat Pro (I have Acrobat Pro 11 but it should work with other versions). I add fields and pre-fill the field's value (not the field name!) with a 'code' (unique text string). This code will be find/replace with the string you want by the javascript function below so let say '%address%' (you can add multiple fields but use a different code to differenciate fields). If you want to have a flatten look of the field, set the field to read-only. To save it, go to File -> Save as other... -> Optimized PDF and choose "Acrobat 6.0 and later" under Make compatible with (top right of the pop-up).
Once you have your file saved, you can check that the format is right by opening it in a text editor and looking for your codes (in my case '%address%'). Count number of occurences, it should appear three times.
The following function does three things:
- Changes the fields content
- Recalculate the content's length
- Fix the cross reference tables
So now the function (look at the end for the final pdf blob):
#param certificate: your pdf form (format of this variable must be compatible with FileReader)
#param changes: the field changes, [{find: '%address%', replace: '2386 5th Street, New York, USA'}, ...]
// Works only for PDF 1.5 (Acrobat 6.0 and later)
var fillCertificate = function (certificate, changes) {
// replace a a substring at a specific position
String.prototype.replaceBetween = function(start, end, what) {
return this.substring(0, start) + what + this.substring(end);
};
// format number with zeros at the beginning (n is the number and length is the total length)
var addLeadingZeros = function (n, length) {
var str = (n > 0 ? n : -n) + "";
var zeros = "";
for (var i = length - str.length; i > 0; i--)
zeros += "0";
zeros += str;
return n >= 0 ? zeros : "-" + zeros;
}
// Create the reader first and read the file (call after the onload method)
var reader = new FileReader();
// To change the content of a field, three things must be done; - change the text of the field, - change the length of the content field, - change the cross table reference
reader.onload = function(aEvent) {
var string = aEvent.target.result;
// Let's first change the content and the content's length
var arrayDiff = [];
var char;
for(var foo = 0; foo < changes.length; foo++) {
// Divide the string into a table of character for finding indices
char = new Array(string.length);
for (var int = 0; int < string.length; int++) {
char[int] = string.charAt(int);
}
// Let's find the content's field to change and change it everywhere
var find = changes[foo].find;
var replace = changes[foo].replace;
var lengthDiff = replace.length - find.length;
var search = new RegExp(find, "g");
var match;
var lastElements = [];
var int = 0;
var objectLenPos;
var objectLenEnd;
// Each time you change the content, compute the offset difference (number of characters). We'll add it later for the cross tables
while (match = search.exec(string)) {
arrayDiff.push({index: match.index, diff: lengthDiff});
lastElements.push({index: match.index, diff: lengthDiff});
// Find length object
if(int == 0){
var length = 0;
var index;
while(char[match.index - length] != '\r'){
index = match.index - length;
length++;
}
objectLenPos = index + 10;
length = 0;
while(char[objectLenPos + length] != ' '){
length++;
objectLenEnd = objectLenPos + length;
}
}
int++;
}
var lengthObject = string.slice(objectLenPos, objectLenEnd) + ' 0 obj';
var objectPositionStart = string.search(new RegExp('\\D' + lengthObject, 'g')) + lengthObject.toString().length + 2;
var length = 0;
var objectPositionEnd;
while(char[objectPositionStart + length] != '\r'){
length++;
objectPositionEnd = objectPositionStart + length;
}
// Change the length of the content's field
var lengthString = new RegExp('Length ', "g");
var fieldLength;
var newLength;
string = string.replace(lengthString, function (match, int) {
// The length is between the two positions calculated above
if (int > objectPositionStart && int < objectPositionEnd) {
var length = 0;
var end;
while (char[int + 7 + length] != '/') {
length++;
end = int + 7 + length;
}
fieldLength = string.slice(end - length, end);
newLength = parseInt(fieldLength) + lengthDiff;
if (fieldLength.length != newLength.toString().length) {
arrayDiff.push({index: int, diff: (newLength.toString().length - fieldLength.length)});
}
// Let's modify the length so it's easy to find and replace what interests us; the length number itself
return "Length%";
}
return match;
});
// Replace the length with the new one based on the length difference
string = string.replace('Length%' + fieldLength, 'Length ' + (newLength).toString());
string = string.replace(new RegExp(find, 'g'), replace);
}
// FIND xref and repair cross tables
// Rebuild the table of character
var char = new Array(string.length);
for (var int = 0; int < string.length; int++) {
char[int] = string.charAt(int);
};
// Find XRefStm (cross reference streams)
var regex = /XRefStm/g, result, indices = [];
while ( (result = regex.exec(string)) ) {
indices.push(result.index);
}
// Get the position of the stream
var xrefstmPositions = [];
for(var int = 0; int < indices.length; int++){
var start;
var length = 0;
while(char[indices[int] - 2 - length] != ' '){
start = indices[int] - 2 - length;
length++;
}
var index = parseInt(string.slice(start, start + length));
var tempIndex = parseInt(string.slice(start, start + length));
// Add the offset (consequence of the content changes) to the index
for(var num = 0; num < arrayDiff.length; num++){
if(index > arrayDiff[num].index){
index = index + arrayDiff[num].diff;
}
}
string = string.replaceBetween(start, start + length, index);
// If there is a difference in the string length then update what needs to be updated
if(tempIndex.toString().length != index.toString().length){
arrayDiff.push({index: start, diff: (index.toString().length - tempIndex.toString().length)});
char = new Array(string.length);
for (var int = 0; int < string.length; int++) {
char[int] = string.charAt(int);
};
}
xrefstmPositions.push(index);
}
// Do the same for non-stream
var regex = /startxref/g, result, indices = [];
while ( (result = regex.exec(string)) ) {
indices.push(result.index);
}
for(var int = 0; int < indices.length; int++){
var end;
var length = 0;
while(char[indices[int] + 11 + length] != '\r'){
length++;
end = indices[int] + 11 + length;
}
var index = parseInt(string.slice(end - length, end));
var tempIndex = parseInt(string.slice(end - length, end));
for(var num = 0; num < arrayDiff.length; num++){
if(index > arrayDiff[num].index){
index = index + arrayDiff[num].diff;
}
}
string = string.replaceBetween(end - length, end, index);
if(tempIndex.toString().length != index.toString().length){
arrayDiff.push({index: end - length, diff: (index.toString().length - tempIndex.toString().length)});
char = new Array(string.length);
for (var int = 0; int < string.length; int++) {
char[int] = string.charAt(int);
};
}
xrefstmPositions.push(index);
}
xrefstmPositions.reverse();
var firstObject, objectLength, end;
var offset;
// Updated the cross tables
for(var int = 0; int < xrefstmPositions.length; int++) {
var length = 0;
var end;
if(char[xrefstmPositions[int]] == 'x'){
offset = 6;
} else{
offset = 0;
}
// Get first object index (read pdf documentation)
while(char[xrefstmPositions[int] + offset + length] != ' '){
length++;
end = xrefstmPositions[int] + offset + length;
}
firstObject = string.slice(end - length, end);
// Get length of objects (read pdf documentation)
length = 0;
while(char[xrefstmPositions[int] + offset + 1 + firstObject.length + length] != '\r'){
length++;
end = xrefstmPositions[int] + offset + 1 + firstObject.length + length;
}
objectLength = string.slice(end - length, end);
// Replace the offset by adding the differences from the content's field
for(var num = 0; num < objectLength; num++){
if(char[xrefstmPositions[int]] == 'x'){
offset = 9;
} else{
offset = 3;
}
// Check if it's an available object
if (char[xrefstmPositions[int] + 17 + offset + firstObject.length + objectLength.length + (num * 20)] == 'n') {
var objectCall = (parseInt(firstObject) + num).toString() + " 0 obj";
var regexp = new RegExp('\\D' + objectCall, "g");
var m;
var lastIndexOf;
// Get the last index in case an object is created more than once. (not very accurate and can be improved)
while (m = regexp.exec(string)) {
lastIndexOf = m.index;
}
string = string.replaceBetween(xrefstmPositions[int] + offset + firstObject.length + objectLength.length + (num * 20), xrefstmPositions[int] + 10 + offset + firstObject.length + objectLength.length + (num * 20), addLeadingZeros(lastIndexOf + 1, 10));
}
if(num == objectLength - 1){
if (char[xrefstmPositions[int] + offset + firstObject.length + objectLength.length + ((num + 1) * 20)] != 't'){
xrefstmPositions.push(xrefstmPositions[int] + offset + firstObject.length + objectLength.length + ((num + 1) * 20));
}
}
}
}
// create a blob from the string
var byteNumbers = new Array(string.length);
for (var int = 0; int < string.length; int++) {
byteNumbers[int] = string.charCodeAt(int);
}
var byteArray = new Uint8Array(byteNumbers);
var blob = new Blob([byteArray], {type : 'application/pdf'});
// Do whatever you want with the blob here
};
reader.readAsBinaryString(certificate);
}
So the code is not clean at all but it works :)
Let me know if you have any question
pdf-lib was the best choice for me. Look, after trying many random solutions I found this lib that's very very simple to implement and, off-topic, has a great TS support (nowadays it means a lot 😬).
Fill form official example: https://pdf-lib.js.org/#fill-form
As far as I can see, there is no client-side application on tablets which would do that.
That means you will need server-side support, and iText is indeed one of the products out there. Another one is FDFMerge by Appligent, which does fill and can be set to flattening.