1

I have a two lists: One that is dynamic, based off of a recordcount of students and the other is the students..i.e., 001,002,003, etc. and 123456 (student data). I need some help being able to randomly assign students one of those numbers. For example, if I have 5 students (123456,234561, etc.), I need to be able to randomly assign 001,002, etc. to those students. So far, hitting a wall. The following is what I have so far:

<cfquery name="testjd" datasource="student">
    SELECT SoonerID FROM dbo.CurrentStudentData
    WHERE status = 'Student' OR status = 'JD'
</cfquery>

<cfset testList = valueList(testjd.soonerid)>
<cfset totalRecordsJd = #testjd.recordcount#>
<cfloop from="001" to="#totalRecordsJd#" index="i">
    <cfset examNo = i>
    <cfif len(i) is 1>
        <cfset examNo = "00" & i>
    <cfelseif len(i) is 2>
        <cfset examNo = "0" & i>
    </cfif>
    <cfif not listFind(usedJDExams, examNo)> 
        #examNo#<!---is NOT being used!---><br/>
    </cfif>
</cfloop>
Shawn
  • 4,758
  • 1
  • 20
  • 29

3 Answers3

1

CF9 makes it a little less fun than later versions. I believe this should work (except for my query mockup).

https://trycf.com/gist/3667b4a650efe702981cb934cd325b08/acf?theme=monokai

First, I create the fake query.

<!--- 
    Simple faked up query data. This is just demo data. I think this 
    queryNew() syntax was first available in CF10.  
--->
<cfscript>
    testjd = queryNew("SoonerID", "varchar", [
        ["123456"],
        ["564798"],
        ["147258"],
        ["369741"]
    ]);
</cfscript>

Now that I've got a list of students who need tests, I create an array of numbers for those tests.

<!--- Create array of Available Exam Numbers --->
<cfset examNos = ArrayNew(1)>
<cfloop from=1 to=100 index="i">
    <cfset examNos[i] = right('00' & i,3)>
</cfloop>

We now combine the two sets of data to get the Exam Numbers assigned to the Student.

<!--- Loop through the query and build random assignments --->
<cfloop query="#testjd#">
    <!---Get random exam no.--->
    <cfset examNo = examNos[randrange(1,arrayLen(examNos))]> 
    <!---Build struct of exam assignments--->
    <cfset testAssignments[SoonerID] = examNo>
    <!---Delete that num from exam array. No reuse.--->
    <cfset blah = arrayDelete(examNos,examNo)>
</cfloop>

And this gives us

<cfoutput>
    <cfloop collection="#testAssignments#" item="i">
        For User #i#, the test is #testAssignments[i]#.<br>
    </cfloop>
</cfoutput>

The unused tests are: <cfoutput>#ArrayToList(examNos)#</cfoutput>.

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

For User 369741, the test is 054. 
For User 147258, the test is 080. 
For User 564798, the test is 066. 
For User 123456, the test is 005. 
The unused tests are: 
     001,002,003,004,006,007,008,009,010
    ,011,012,013,014,015,016,017,018,019,020
    ,021,022,023,024,025,026,027,028,029,030
    ,031,032,033,034,035,036,037,038,039,040
    ,041,042,043,044,045,046,047,048,049,050
    ,051,052,053,055,056,057,058,059,060
    ,061,062,063,064,065,067,068,069,070
    ,071,072,073,074,075,076,077,078,079
    ,081,082,083,084,085,086,087,088,089,090
    ,091,092,093,094,095,096,097,098,099,100.

A couple of code review notes for the OP code:

1) It's easier to work with arrays or structures than it is to work with a list.

2) cfloop from="001" to="#totalRecordsJd#": from "001" is a string that you are comparing to an integer. ColdFusion will convert "001" to a number in the background, so that it can actually start the loop. Watch out for expected data types, and make sure you use arguments as they were intended to be used.

3) cfif len(i) is 1...: First, it's less processing to build this string in one pass and them trim it - right('00' & i,3). Second (and this is a personal nitpick), is and eq do essentially the same thing, but I've always found it good practice to apply is to string-ish things and eq to number-ish things.

=====================================================================

For CF10+, I would use something like

https://trycf.com/gist/82694ff715fecd328c129b255c809183/acf2016?theme=monokai

<cfscript>
    // Create array of random string assignments for ExamNo
    examNos = [] ;
    for(i=1; i lte 100;i++) {
        arrayAppend(examNos,right('00'& i,3)) ;
    }

    ///// Now do the work. ////
    //Create a struct to hold final results
    testAssignments = {} ;
    // Loop through the query and build random assignments
    for(row in testjd) {
        // Get random exam no.
        examNo = examNos[randrange(1,arrayLen(examNos))] ; 
        // Build struct of exam assignments
        structInsert(testAssignments, row.SoonerID, examNo) ; 
        // Delete that num from exam array. No reuse.
        arrayDelete(examNos,examNo) ; 
    }
</cfscript>
Shawn
  • 4,758
  • 1
  • 20
  • 29
  • Shawn, I can guarantee you....100%....that I did not downvote anything. Trust me, everyone's advice has been helpful. – John Eubanks Nov 21 '17 at 20:34
  • @JohnEubanks LOL, no worries. I just saw that I entered the answer and within a couple of minutes, someone had downvoted your question and both answers on the question. Gotta love serial downvoters. :-/ – Shawn Nov 21 '17 at 20:40
  • Shawn, final question on this: in order to perform an insert into a table, do I need to convert this back to a list? – John Eubanks Nov 21 '17 at 20:52
  • Where does the list of `ExamNo`s come from? You possibly don't need to come out of the database for this if the exam numbers come from there in the first place. Are you inserting into another database field? Can you provide a simple schema of your database structure for these fields? – Shawn Nov 21 '17 at 21:16
  • To update your database with a test for the students: http://sqlfiddle.com/#!6/ee17d/1/0 It seems a little messy to me, but it works for small batches. I haven't tested with larger sets. NEWID() can slow the query down. This update doesn't exclude a test that has been used, and it may seem non-random-ish for small test sets. It will only pull from the list of active tests and only update the list of current Students. You can refresh the UPDATE to see how it changes. – Shawn Nov 22 '17 at 19:33
  • My above update assumes that active ExamNos come from a database table. – Shawn Nov 22 '17 at 19:34
1

If it is a small query, why not just sort the records (psuedo) randomly using NEWID()? Since the records will already be randomized, you cna use query.currentRow to build the "examNo".

<cfquery name="testjd" datasource="student">
    SELECT SoonerID 
    FROM   CurrentStudentData
    WHERE  status IN ('Student', 'JD')
    ORDER BY NewID()
</cfquery>

<cfoutput query="yourQuery">
   #yourQuery.SoonerID# #NumberFormat(yourQuery.currentRow, "000000")#<br>
</cfoutput>
SOS
  • 6,430
  • 2
  • 11
  • 29
  • Shawn raises a good question... are these really random numbers? If they're actually id's from a db table there's shorter ways to do the insert.. – SOS Nov 21 '17 at 22:27
-1

This is a formatted comment. After you run this query:

<cfquery name="testjd" datasource="student">
    SELECT SoonerID FROM dbo.CurrentStudentData
    WHERE status = 'Student' OR status = 'JD'
</cfquery>

Do this:

<cfset QueryAddColumn(testJd, "newcolumn", arrayNew(1))>

Then loop through the queries and assign values to the new column with QuerySetCell.

Dan Bracuk
  • 20,699
  • 4
  • 26
  • 43