You could name the fields with a counter variable. So the
field names are consecutive: quantity_1, product_1, quantity_2,
product_2, ... The total number of fields should be stored in a
hidden field, outside the loop.
<cfloop ....>
<input type="hidden" name="productID_#counter#"
value="#productID#">
<input type="hidden" name="quantity_#counter#"
value="">
</cfloop>
..
<input type="hidden" name="numberOfFields" value="...">
Then you can use the total number of fields in a loop to
iterate through each set of form fields.
<cfparam name="form.numberOfFields" default="0">
<cfoutput>
<cfloop from="1" to="#form.numberOfFields#" index="x">
<cfset variables.productID = form["productID_"&
x]>
<cfset variables.quantity = form["quantity_"& x]>
<cfif len(trim(variables.quantity)>
The form.quantity_#x# = #variables.quantity#<br>
</cfif>
</cfloop>
</cfoutput>
But that is just one option. There are others.
Edit: You could also use list functions instead of multiple
comparisons. For example
<cfif not listFindNoCase("submit,did,fieldnames",
varname)>
...
</cfif>