After my post yesterday on selecting or rotating images, a reader asked if it was possible to show a random image once and not again until the other images are shown. That is certainly possible, and here is one way to do it...
<cfapplication name="img" sessionManagement="true">
<!--- Get full path to images. --->
<cfset imageDirectory = expandPath(".")>
<!--- Get directory --->
<cfdirectory action="list" directory="#imageDirectory#" name="images" filter="*.jpg">
<!--- Do we have any images? --->
<cfif images.recordCount gt 0>
<!--- store ID values --->
<cfif not structKeyExists(session, "totalList") or session.totalList is "">
<cfset session.totalList = valueList(images.name)>
</cfif>
<!--- pick a random number --->
<cfset pickedIndex = randRange(1, listLen(session.totalList))>
<!--- pick from list --->
<cfset image = listGetAt(session.totalList, pickedIndex)>
<!--- remove from total list --->
<cfset session.totalList = listDeleteAt(session.totalList, pickedIndex)>
<!--- display it --->
<cfoutput><img src="#image#"></cfoutput>
</cfif>
The way I handled it was to simply store the list of filenames in a session variable. This then becomes my list of data to randomly select from. Because the list gets smaller on every hit, I have to check for either the session variable not existing, or if it is empty, and if so, I fill in the values.
Running this version will give you a random order of images, with no image repeated until they have all been shown. There is one exception to this. It is possible that if dharma.jpg, for example, was picked last, that it could then be picked first on reload. As homework, modify the code above to handle that edge case.
p.s. As a reminder, do not forget that you can subscribe to the blog by using the Subscribe form on the right hand side. This will let you get entries via email.
Archived Comments
would this work? i know people don't like using cfbreak, but it seems like it would work. i'm probably missing a much easier solution here.
<cfapplication name="img" sessionManagement="true">
<!--- Get full path to images. --->
<cfset imageDirectory = expandPath(".")>
<!--- Get directory --->
<cfdirectory action="list" directory="#imageDirectory#" name="images" filter="*.jpg">
<!--- Do we have any images? --->
<cfif images.recordCount gt 0>
<!--- store ID values --->
<cfif not structKeyExists(session, "totalList") or session.totalList is "">
<cfset session.totalList = valueList(images.name)>
</cfif>
<!--- pick a random number --->
<cfset pickedIndex = randRange(1, listLen(session.totalList))>
<!--- if the random number is equal to the last index and we're at the beginning of the list --->
<cfif structKeyExists(application.lastIndex) and application.lastIndex eq pickedIndex and listLen(session.totalList) eq images.recordCount>
<cfloop from="1" to="#listLen(session.totalList)#" index="i">
<cfset pickedIndex = randRange(1, listLen(session.totalList))>
<cfif pickedIndex neq application.lastIndex>
<cfbreak>
</cfif>
</cfloop>
</cfif>
<!--- pick from list --->
<cfset image = listGetAt(session.totalList, pickedIndex)>
<!--- remove from total list --->
<cfset session.totalList = listDeleteAt(session.totalList, pickedIndex)>
<!--- set an application variable containing the last index in the list so we don't repeat --->
<cfif listLen(session.totalList) eq 1>
<cfset application.lastIndex = pickedIndex>
</cfif>
<!--- display it --->
<cfoutput><img src="#image#"></cfoutput>
</cfif>
or a conditional loop?
<cfloop condition="pickedIndex neq application.lastIndex">
<cfset pickedIndex = randRange(1,listLen(session.totalList))>
</cfloop>
should be session.lastIndex, not application.
if the image name was stored in the client scope, then would it also prevent repeating across sessions?
<!--- Get full path to images. --->
<cfset imageDirectory = "#rootpath#img">
<!--- Get directory --->
<cfdirectory action="list" directory="#imageDirectory#" name="images" filter="*.jpg">
<!--- Do we have any images? --->
<cfif images.recordCount gt 0>
<!--- store ID values --->
<cfif not structKeyExists(session, "totalList") or session.totalList is "">
<cfset session.totalList = valueList(images.name)>
</cfif>
<!--- remove last picked from the list --->
<cfif structKeyExists(client, "imagepicked")>
<cfset session.totalList = listDeleteAt(session.totalList, listfind(session.totalList,client.imagepicked))>
<!--- at end of list, repopulate excluding last picked --->
<cfif session.totalList is "">
<cfset session.totalList = listDeleteAt(valueList(images.name), listfind(valueList(images.name),client.imagepicked))>
</cfif>
</cfif>
<!--- pick a random number --->
<cfset pickedIndex = randRange(1, listLen(session.totalList))>
<!--- pick from list --->
<cfset client.imagepicked = listGetAt(session.totalList, pickedIndex)>
<!--- display it --->
<cfoutput><img src="#basehref#img/#client.imagepicked#"></cfoutput>
</cfif>
Yet another way using only a cookie as the holder and QoQ to filter the images already shown to the user. Yes I know that cookies have a limit of 4K for the domain, but this is just an example.
<!--- Get full path to images. --->
<cfset imageDirectory = expandPath("../images")>
<!--- Get directory --->
<cfdirectory action="list" directory="#imageDirectory#" name="images" filter="*.jpg">
<!--- Do we have any images? --->
<cfif images.recordCount gt 0>
<!--- create a cookie if we don't have one OR reset the cookie if we've shown all the images' --->
<cfif not structKeyExists(cookie, "imageList") OR ListLen(cookie.imageList, "|") EQ images.recordCount>
<cfcookie name="imageList" expires="never" value="">
</cfif>
<!--- remove images already shown --->
<cfquery name="imagesQuery" dbtype="query">
SELECT name FROM images
<cfif cookie.imageList NEQ "">
WHERE
1 = 1
<cfloop list="#cookie.imageList#" delimiters="|" index="i">
AND name <> <cfqueryparam value="#i#">
</cfloop>
</cfif>
</cfquery>
<!--- only if we have left over images --->
<cfif imagesQuery.RecordCount GT 0>
<!--- pick the lucky image --->
<cfset imagePicked = imagesQuery.name[RandRange(1, imagesQuery.RecordCount)]>
<!--- append to the cookie --->
<cfset cookie.imageList = ListAppend(cookie.imageList, imagePicked, "|")>
<!--- display it --->
<cfoutput><img src="../images/#imagePicked#"></cfoutput>
</cfif>
</cfif>
How about using this same code and implementing a linkable image?
Joe, what do you mean? You mean linking to the image instead of showing it? That's just an HTML change and would be trivial.
Ray,
I just want to attach a link to the image. I have specific links for each image. Ex. I click on Home Depot's image to get to Home Depot's website.
Well, that is getting more complex. Our current code is based on physical files, but if you want other info along with the images, you most likely want to switch to a database approach.
Picking a two random numbers from a list, the list may not be repeated suppose
1 student has 10 books ( 1, 2,3,4,5,6,7,8,9 ,10)
2 student has 9 ( 11,22,13,14,15,16,17,18,19)
3 student has 7 ( 20,21,23,24,24,25,26).All this books are in list if I were to pick two random books for each student. How can that be done. The example above deletes the picked number, here I need to delete the list for eg 1 studnet, start from 2nd student.