Skip to main content
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Yancy Wharton
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Yancy Wharton@ywharton )

Selecting The Seven Languages In Seven Weeks Winners

By on
Tags:

About a week or so ago, I finished working my way through Seven Languages in Seven Weeks by Bruce Tate. To celebrate my mostly triumphant reading of the book (shakes fist at Haskell, Day 3), I wanted to give away a few copies. But, in order to select the winners of the book, I wanted to have a little fun and try to play with the Mapping concepts that were often discussed within the book.

Given an array of players (those that submitted sample code on my wrap-up), I created an ArrayMap() function and a function that would randomly return a given value. The idea was to continue to reduce the collection of players until only 2 were left.

<!--- Set up the array of players. --->
<cfset players = [
	"Wilkins",
	"Mike",
	"Seth Stone",
	"Guganeshan.T",
	"Joshua Caito",
	"Joe Zack",
	"Andrew",
	"Rich",
	"Hidde-Jan",
	"dotnetCarpenter",
	"Adam Coffman",
	"Owen Pellegrin",
	"Ross Pfahler",
	"Marty",
	"Jared Cacurak",
	"Morten",
	"Greg Mefford",
	"Tracy Harms",
	"Eric Mitchell",
	"Jesse Riggins",
	"Danny Philayvanh",
	"Sharagoz",
	"Joel Neley",
	"Adam",
	"Nelle",
	"Matt Gutting",
	"Cade Cannon"
	] />


<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->


<!---
	Define a map function that maps one array onto another using
	the given function. When mapping, the following values will be
	handles as such:

	- Array - will be flattened and merged into the resultant array.
	- Value - will be added to the resultant array.
	- VOID - will not be added ot the resultant array.
--->
<cffunction
	name="arrayMap"
	access="public"
	returntype="array"
	output="false"
	hint="I map one array onto another array.">

	<!--- Define arguments. --->
	<cfargument
		name="originalArray"
		type="array"
		required="true"
		hint="I am the array being mapped."
		/>

	<cfargument
		name="mapper"
		type="any"
		required="true"
		hint="I am the function used to perform the mapping."
		/>

	<!--- Define the local scope. --->
	<cfset var local = {} />

	<!--- Define an array to hold the results of the mapping. --->
	<cfset local.results = [] />

	<!--- Loop over the array so we can map each value. --->
	<cfloop
		index="local.originalValue"
		array="#arguments.originalArray#">

		<!--- Hand this value off to the mapper function. --->
		<cfset local.mappedValue = arguments.mapper(
			local.originalValue
			) />

		<!---
			Check to make sure that a value exists. If it doesn't,
			it means that VOID was returned; as such, no mapped
			value will be added to the results.
		--->
		<cfif structKeyExists( local, "mappedValue" )>

			<!---
				We know we have a mapped value, so let's not figure
				out how we are merging it into the results array.
				Arrays will be flattened; single values will be
				added as such.
			--->
			<cfif isArray( local.mappedValue )>

				<!--- Add each value of the array individually. --->
				<cfloop
					index="local.mappedSubValue"
					array="#local.mappedValue#">

					<!--- Add the flattened values. --->
					<cfset arrayAppend(
						local.results,
						local.mappedSubValue
						) />

				</cfloop>

			<cfelse>

				<!--- Simply add the returned value. --->
				<cfset arrayAppend(
					local.results,
					local.mappedValue
					) />

			</cfif>

		</cfif>

	</cfloop>

	<!--- Return the resultant array. --->
	<cfreturn local.results />
</cffunction>


<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->


<!---
	Now that we have our map function defined, we need to create a
	function that actually provides the mapping logic. For our
	purposes, we're going to keep mapping the players array onto a
	new array until we only have 2 people left. That means that our
	mapper function will be popping people out of the collection.
--->
<cffunction
	name="returnRandom"
	access="public"
	returntype="any"
	output="false"
	hint="I randomly return the given value or VOID.">

	<!--- Define arguments. --->
	<cfargument
		name="value"
		type="any"
		required="true"
		hint="I am the value being returned (maybe!)."
		/>

	<!---
		Randomly decide to return this value or not. If it is
		returned, it will be mapped onto the results array; if it
		is not, it will be removed.
	--->
	<cfif (rand() gt .25)>

		<!--- Return the given value. --->
		<cfreturn arguments.value />

	<cfelse>

		<!--- Return void. --->
		<cfreturn />

	</cfif>
</cffunction>


<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->


<!---
	Now that we have all of our mapping functions in place, let's
	reduce our players collection down to a collection of 2. As we
	map, there is a chance that we will drop below 2. In that case,
	we'll have to just start over again.
--->
<cfoutput>


	<!---
		Keep looping until we stop. If we get a collection of 2,
		we will execute an explicit Break command.
	--->
	<cfloop condition="true">

		<!--- Copy our players array (set by VALUE). --->
		<cfset winners = players />


		<!--- Output the staring length. --->
		Starting Length: #arrayLen( winners )#<br />


		<!---
			Keep reducing the collection until it is less than
			or equal to 2 (we are hoping it is 2).
		--->
		<cfloop condition="(arrayLen( winners ) gt 2)">

			<!--- Reduce the collection randomly. --->
			<cfset winners = arrayMap(
				winners,
				returnRandom
				) />

			<!--- Output the new length. --->
			Current Length: #arrayLen( winners )#<br />

		</cfloop>


		<!--- Check to see if the collection contains 2 values. --->
		<cfif (arrayLen( winners ) eq 2)>

			<!---
				We have reduced properly. Stop the reduction loop
				as we have found our winners.
			--->
			<cfbreak />

		</cfif>

	</cfloop>


	<hr />

	<!--- Output the winners. --->
	Winner: #winners[ 1 ]#<br />
	Winner: #winners[ 2 ]#<br />

</cfoutput>

Since the reduction mapping might result in an array of length less than 2, I had to continually loop until the final array contained exactly two elements. When I ran the above code, I got the following page output:

Starting Length: 27
Current Length: 18
Current Length: 15
Current Length: 13
Current Length: 11
Current Length: 11
Current Length: 5
Current Length: 3
Current Length: 3
Current Length: 1
Starting Length: 27
Current Length: 21
Current Length: 16
Current Length: 10
Current Length: 8
Current Length: 8
Current Length: 7
Current Length: 7
Current Length: 7
Current Length: 5
Current Length: 1
Starting Length: 27
Current Length: 22
Current Length: 21
Current Length: 13
Current Length: 10
Current Length: 6
Current Length: 6
Current Length: 5
Current Length: 5
Current Length: 3
Current Length: 3
Current Length: 2

Winner: Jared Cacurak
Winner: Matt Gutting

As you can see, the reduction mapping had to run 3 times before it ended up with an array of length 2. Of course, I know there are much more efficient ways of doing this; but, efficiency wasn't really the point - I just wanted to have some fun with it.

Congratulations to Jared and Matt! You're about to get seven languages deep!

Want to use code from this post? Check out the license.

Reader Comments

14 Comments

Stupid ColdFusion!
I thought the point of your blog was to make me like ColdFusion, now I am not so sure it will every happen :)

/A Loser

3 Comments

Grats to the winners!

Thanks again for your creativity with the contest Ben. It would be cool to do something like this every once in a while even if there is no prize involved :)