getIntsFromRangeString UDF for ColdFusion
Tags: cfml, coldfusion
Published: 2022-09-14 00:13:31

## Useful for validating a human-entered list of integers.

I was working on a search interface for a client where public visitors would be searching for a range of different values regarding event years, event place, ago, # of competitions, hours of training etc.  I initially searched Google for "[range slider](https://www.google.com/search?q=Range+Slider)" and found many interactive widgets that I could've added to the interface. I didn't personally like the experience as each slider represented a different range and it seemed weird to see them next to each other as they didn't represent the same thing.

Another option, initially proposed by the client, was to add a start & end drop-downs.  This would mean generating hundreds of hidden `<option>` values so the visitor could make their selection... and then require additional client-side logic to chain the selects together so that the max wouldn't be less than the min (and vice-versa).  I didn't like this either as it requires a lot of mouse & mental interaction to select + keep in sync with each other.  (I personally prefer typing, pasting or leveraging browser extensions to save+autofill whenever possible.)

I thought it would be best to allow the user to type anything that they wanted, limit which characters could be entered (`0-9`, `,`, `\s` and `-`), sanitize values upon `onchange` and perform the logic server-side to parse the numbers that were provided to both generate a list of integers (for use in a SQL `IN` query) and display visually back to the visitor.  I found [formatListAsSeries UDF](https://cflib.org/udf/formatListAsSeries) on CFLib (from 2010) that accepts a numeric list and generates a series for visual display... all I needed to do is figure out how to do the exact opposite AND enforce logical min/max rules.

Here's my attempt...
```html
<cfset tests = [
	["numberList": "0, 2021- , -1978, 1980-1984,1988, 1999, 8888, abc, 1920, 1E3", "minInt":1977, "maxInt":2022]
	,["numberList": "2,1,6,,7,8,2, 2, 10, 1-3, 9.5", "minInt":1, "maxInt":10]
	,["numberList": "0, 6, 4, 22, 100-200, 5,  😀  ", "minInt":0, "maxInt":10]
]>

<cfoutput>
	<h3>getIntsFromRangeString(numberList, <i>minInt</i>, <i>maxInt</i>)</h3>
	<cfloop array="#tests#" index="test">
		<cfset a = racerSearch.getIntsFromRangeString(argumentcollection=test)>
		<b>getIntsFromRangeString("#test.numberList#", <i>#test.minInt#</i>, <i>#test.maxInt#</i>)</b><br>
		<b>SortedValues:</b> <tt>#serializeJson(a.sortedValues)#</tt><br>
		<b>SeriesString:</b> <tt>"#a.seriesString#"</tt>
		<hr>
	</cfloop>
</cfoutput>
```
This generates the following output:
```
getIntsFromRangeString("0, 2021- , -1978, 1980-1984,1988, 1999, 8888, abc, 1920, 1E3", 1977, 2022)
SortedValues: [1977,1978,1980,1981,1982,1983,1984,1988,1999,2021,2022]
SeriesString: "1977-1978, 1980-1984, 1988, 1999, 2021-2022"
------------------------------------
getIntsFromRangeString("2,1,6,,7,8,2, 2, 10, 1-3, 9.5", 1, 10)
SortedValues: [1,2,3,6,7,8,10]
SeriesString: "1-3, 6-8, 10"
--------------------------------
getIntsFromRangeString("0, 6, 4, 22, 100-200, 5, 😀 ", 0, 10)
SortedValues: [0,4,5,6]
SeriesString: "0, 4-6"
```

Check it out and let me know if I forgot anything. Enjoy!

2022-09-20 Update: I've made some updates for performance as well as a SQL string generator.

## CFML Source Code

https://gist.github.com/JamoCA/db753001d64a6fd4d46d2f591769c589

{% gist https://gist.github.com/JamoCA/db753001d64a6fd4d46d2f591769c589 %}