Jump to content

Random Facts


brians

Recommended Posts

I'm attempting to randomly fill 2 out of 8 possible facts into my layout. I've tried to pick apart the Bingo template without much luck. Each fact will feature a variable within the string, but this part hasn't been a problem. The problem I'm having is I don't want the facts to duplicate on a single record. Does anyone out there have a suggestion on how to approach this. Right now I'm running a random number generator as a single rule and then reference this rule in two other 2 rules. Below are the two rules (I've simplified the facts for the example). To avoid duplicates I'm pulling the facts from two seperate pools of facts. It works but isn't a prefered method as it limits the possabilities and requires more facts. Any help would be greatly appreciated.

 

if (Rule("Number1") == "0")

{

return RawText("Fact 1");

}

if (Rule("Number1") == "1")

{

return RawText("Fact 2");

}

if (Rule("Number1") == "2")

{

return RawText("Fact 3");

}

if (Rule("Number1") == "3")

{

return RawText("Fact 4");

}

else

{

return RawText("Fact 5");

}

return "";

 

-----------------------------------

 

if (Rule("Number1") == "1")

{

return RawText("Fact 6");

}

if (Rule("Number1") == "2")

{

return RawText("Fact 7");

}

if (Rule("Number1") == "3")

{

return RawText("Fact 8");

}

if (Rule("Number1") == "0")

{

return RawText("Fact 9");

}

else

{

return RawText("Fact 10");

}

return "";

-----------------------------------

Link to comment
Share on other sites

Hi Brian,

 

If I understand correctly what you're trying to accomplish, I think this code should be able to help you:

 

var facts = ["fact1","fact2","fact3","fact4","fact5","fact6","fact7","fact8"]; // Create an array of facts
var result = []; // Create an array to hold the facts at random

for (var i=0; i<2; i++){
   var number = Math.floor(Math.random()*facts.length); // generate a random number between 0 and length of the array
   result.push(facts[number]); // push the fact into the result array
   facts.splice(number,1); // remove that fact from the Facts array so that it won't be pulled again
}

return result; 

Link to comment
Share on other sites

One final sticking point. I need each fact on a seperate line without the commas. I added \r\n to add the returns but connot seem to get rid of the commas.

 

var facts = ["fact1","fact2","fact3","fact4","fact5","fact6","fact7","fact8"]; // Create an array of facts

var result = []; // Create an array to hold the facts at random

for (var i=0; i<3; i++){

var number = Math.floor(Math.random()*facts.length); // generate a random number between 0 and length of the array

result.push(facts[number]+"\r\n"); // push the fact into the result array

facts.splice(number,1); // remove that fact from the Facts array so that it won't be pulled again

}

return result;

Link to comment
Share on other sites

Thanks step. I'm on to the next template which uses the same type of random facts however for this one I need to return the facts to seperate frames and flowing them from one frame to the next won't work due to variable lengths and being multi-line in some cases. Is there a way to access the random facts on an individual basis from the original rule below?
Link to comment
Share on other sites

Put the rule in an OnRecordStart callback rule and remove the term "var" from the 2nd line. Then you can call custom-named text frames from OnRecordStart to auto-populate the various frames in your template, or create multiple text rules that return the various elements from the global array.
Link to comment
Share on other sites

Can you give an example of how one of the text rules might look? This is where I'm having difficulty is in understanding how to grab just the first fact or just the second. Actually I thought I had a way in using the commas but that became a problem when the fact contained a comma.
Link to comment
Share on other sites

Sure.

 

you can call custom-named text frames from OnRecordStart to auto-populate the various frames in your template

 

FindTextFrame("Fact1").content = result[0];
FindTextFrame("Fact2").content = result[1];

 

Where the text frames are named "Fact1" and "Fact2" respectively. You access the elements within the array with the brackets and define its position. 0 is the first position, 1 is the second, etc. In this case, the array only has two of the positions filled (your two random facts).

 

or create multiple text rules that return the various elements from the global array

 

Similarly, you could create a rule called "Fact1" (for example) and return the first position in the array:

 

return result[0];

Link to comment
Share on other sites

  • 1 month later...

I'm now trying to add one more level of complexity to this project. We want to take this script and make it possible for the orderer to fill in up to 20 facts but only make 5 required. If they leave a fact blank the script would skip it and randomly place only those that contain text. Below is the script in it's current state. Can one of you please assist?

 

FusionPro.Composition.repeatRecordCount = Field("Quantity");

var facts = [TaggedDataField("Fact1"),TaggedDataField("Fact2"),TaggedDataField("Fact3"),TaggedDataField("Fact4"),TaggedDataField("Fact5"),TaggedDataField("Fact6"),TaggedDataField("Fact7"),TaggedDataField("Fact8"),TaggedDataField("Fact9"),TaggedDataField("Fact10"),TaggedDataField("Fact11"),TaggedDataField("Fact12"),TaggedDataField("Fact13"),TaggedDataField("Fact14"),TaggedDataField("Fact15"),TaggedDataField("Fact16"),TaggedDataField("Fact17"),TaggedDataField("Fact18"),TaggedDataField("Fact19"),TaggedDataField("Fact20")]; // Create an array of facts

result = []; // Create an array to hold the facts at random

for (var i=0; i<3; i++){

var number = Math.floor(Math.random()*facts.length); // generate a random number between 0 and length of the array

result.push(facts[number]); // push the fact into the result array

facts.splice(number,1); // remove that fact from the Facts array so that it won't be pulled again

}

 

FindTextFrame("Fact1").content = result[0];

FindTextFrame("Fact2").content = result[1];

FindTextFrame("Fact3").content = result[2];

Link to comment
Share on other sites

Not sure if this will work, but how about:

FusionPro.Composition.repeatRecordCount = Field("Quantity");

result = []; // global variable containing random facts
var facts = []; // Create an array for the provided facts

// push only provided fact values into the array
for (f=0; f<20; f++) {
  var currFact = "Fact" + f;
  if (TaggedDataField(currFact) != "") facts.push(TaggedDataField(currFact));
}

for (var i=0; i<5; i++){
  var number = Math.floor(Math.random()*facts.length); // generate a random number between 0 and length of the array
  result.push(facts[number]); // push the fact into the result array
  facts.splice(number,1); // remove that fact from the Facts array so that it won't be pulled again
}

FindTextFrame("Fact1").content = result[0];
FindTextFrame("Fact2").content = result[1];
FindTextFrame("Fact3").content = result[2];
FindTextFrame("Fact4").content = result[3];
FindTextFrame("Fact5").content = result[4];

I added a FOR loop to populate the facts array with only fields that actually contain a value. This way your facts array may contain between 5 (assuming you require 5 filled values) and 20 facts to choose from in step's previous code.

Link to comment
Share on other sites

I had to make on adjustment to get the expression to pass which was, I changed f=0 to f=1 otherwise it would error ("Error: In Field(), no field named Fact0"). Once this was changed the expression passed but empty variables were still returned during preview.
Link to comment
Share on other sites

I had to make on adjustment to get the expression to pass which was, I changed f=0 to f=1 otherwise it would error ("Error: In Field(), no field named Fact0"). Once this was changed the expression passed but empty variables were still returned during preview.

Changing the start point of f from 0 to 1 would only process 19 possible facts for insertion. See if the following helps:

 

FusionPro.Composition.repeatRecordCount = Field("Quantity");

result = []; // global variable containing random facts
var facts = []; // Create an array for the provided facts

// push only provided fact values into the array
for (f=1; f<21; f++) {
  var currFact = "Fact" + f;
  if (TaggedDataField(currFact) != "") facts.push(TaggedDataField(currFact));


for (var i=0; i<5; i++){
  var number = Math.floor(Math.random()*facts.length); // generate a random number between 0 and length of the array
  result.push(facts[number]); // push the fact into the result array
  facts.splice(number,1); // remove that fact from the Facts array so that it won't be pulled again
}

FindTextFrame("Fact1").content = result[0];
FindTextFrame("Fact2").content = result[1];
FindTextFrame("Fact3").content = result[2];
FindTextFrame("Fact4").content = result[3];
FindTextFrame("Fact5").content = result[4];

If this still returns "empty" values, could you upload some sample data to help me determine where I'm going wrong for your specific situation? My guess is that "empty" values still pass something in the field value which causes the IF loop to add false positives.

Edited by esmith
fixed code
Link to comment
Share on other sites

In the sample job you provided, you still have the facts array pre-defined with all 20 facts. If you look at my code, you'll see that the array is initially unpopulated. When I made this change in the sample template, the results looked to be what you are expecting.
Link to comment
Share on other sites

  • 3 years later...

Hello,

 

I was able to use the original code to generate random facts but I need to make an adjustment and wanted to see if someone has a recommendation.

 

This is for a Bingo card where there are questions and the answers will be printed on the Bingo card. So column B should only have 15 answers that are different from the 15 answers in column I for example. I have a data file that has B,I,N,G,O answers separated out and there is a total of 15 answers for each column?

 

Thanks

Rick

Link to comment
Share on other sites

This is for a Bingo card where there are questions and the answers will be printed on the Bingo card. So column B should only have 15 answers that are different from the 15 answers in column I for example. I have a data file that has B,I,N,G,O answers separated out and there is a total of 15 answers for each column?

 

What does your data file look like? Assuming your B,I,N,G, and O fields contain a list of answers separated by a comma, you could do something like this:

var answers = Field("B").split(','); // Create an array of answers
var result = []; // Create an array to hold the facts at random

for (var i=0; i<15; i++){
   var number = Math.floor(Math.random()*answers.length); // generate a random number between 0 and length of the array
   result.push(answers[number]); // push the fact into the result array
   answers.splice(number,1); // remove that fact from the Answers array so that it won't be pulled again
}

return result.join('<br>');

The above code will generate 15 random answers from the 'B' field separated by a break tag (<br>). The idea being that you would apply that rule to a text frame that will make up the "B" column. If you wanted to, you could name all of the "column" text frames by the data field they correspond to (B-O), check "Re-evaluate this rule for every text flow," and apply this rule to each frame:

[color="Red"]var column = FusionPro.Composition.CurrentFlow.name;
var answers = Field(column).split(','); // Create an array of answers[/color]
var result = []; // Create an array to hold the facts at random

for (var i=0; i<15; i++){
   var number = Math.floor(Math.random()*answers.length); // generate a random number between 0 and length of the array
   result.push(answers[number]); // push the fact into the result array
   answers.splice(number,1); // remove that fact from the Answers array so that it won't be pulled again
}

return result.join('<br>');

 

Of course you could use a table as well. If you supply a copy of your template, data, or intended output, I'd be able to give you a better idea of how to accomplish this.

Link to comment
Share on other sites

Rather than drawing 25 text frames, you'd be better off generating a table and displaying it in one big text frame. Here's what that would look like:

// Link as external data file
var ex = new ExternalDataFileEx('BINGO Questions.txt','\t');

// Create an object of each field value
var cols = {};
for (var n=0; n<ex.fieldCount; n++) {
   var col = ex.GetFieldValue(0, n);
   cols[col] = [];
   for (var i=1; i<=ex.recordCount; i++)
       cols[col].push(ex.GetFieldValue(i,col));
}

// Shuffle the fields and then take 5 values
for (var i in cols)
   cols[i] = shuffle(cols[i]).splice(0,5);

cols['N Answers'][2] = ''; // Free space

// Create 1.5" x 1.5" spaces in a 5x5 table
var table = new FPTable;
table.AddColumns(10800,10800,10800,10800,10800)
for (var i=0; i<5; i++) {
   var row = table.AddRow();
   var cell = row.Cells[0];
   row.minHeight = 10800;
   cell.VAlign = "Middle";
   cell.HAlign = "Center";
   cell.SetBorders('Thin','White','Left','Right','Top','Bottom');
   row.CopyCells(0,1,2,3,4);
   row.SetContents(cols['B Answers'][i],cols['I Answers'][i],cols['N Answers'][i],cols['G Answers'][i],cols['O Answers'][i]);
}

return table.MakeTags();





/*============================================ /
|| Shuffle function
|| http://bost.ocks.org/mike/shuffle/
/  ===========================================*/

function shuffle(array) {
 var currentIndex = array.length, temporaryValue, randomIndex ;

 // While there remain elements to shuffle...
 while (0 !== currentIndex) {

   // Pick a remaining element...
   randomIndex = Math.floor(Math.random() * currentIndex);
   currentIndex -= 1;

   // And swap it with the current element.
   temporaryValue = array[currentIndex];
   array[currentIndex] = array[randomIndex];
   array[randomIndex] = temporaryValue;
 }

 return array;
}

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...