Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Selecting The Seven Languages In Seven Weeks Winners

By Ben Nadel on
Tags: ColdFusion

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!


Reader 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

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 :)