I have an array but it could be a list using listtoarray or vise verse is there an easy way to delete all of a particular 'value' from a list or an array. For example I have a list or array with:
And say i want to remove all instances of the number two... is there a simple way to do that?
There are probably a ton of ColdFusion user defined functions out there that already do this, but I figured I would take a crack at it as I figure it will keep my mind sharp (and plus, I always like having a place to point people on future questions). For this problem, let's assume that you want to delete a given value OR a list of values. To accomplish this, I am converting the target list to an array. This will allow us to iterate over the list faster than doing a list loop. I am also converting the value list (that we want to delete) into a struct in which the list values are the struct keys. This will provide a super fast value look-up that will tell us instantly whether or not we have a value we want to delete.
In the interest of speed, I am making some sacrifices. For starters, by converting the target list to an array, we are losing the delimiters. That means that if we have a multi-delimited list, there is no way we can recover the proper delimiters without some serious overhead. Also, by using the struct-key lookup, we can no longer perform a case-sensitive value comparison since ColdFusion struct keys are not case sensitive.
While these are limitations, I don't think they are that bad. If you think about the common scenarios that involve list manipulation, I think you will find that often times it is a single-delimited list and usually involves numeric values which do not have case. Therefore, I think this function is speed-tailored for the general case scenario.
That being said, here is ListDeleteValue( TargetList, ValueList [, Delimiters] ), the ColdFusion user defined function that I came up with:
<cffunction name="ListDeleteValue" access="public" returntype="string" output="false" hint="Deletes a given value (or list of values) from a list. This is not case sensitive."> <!--- Define arguments. ---> <cfargument name="List" type="string" required="true" hint="The list from which we want to delete values." /> <cfargument name="Value" type="string" required="true" hint="The value or list of values that we want to delete from the first list." /> <cfargument name="Delimiters" type="string" required="false" default="," hint="The delimiting characters used in the given lists." /> <!--- Define the local scope. ---> <cfset var LOCAL = StructNew() /> <!--- Create an array in which we will store our new list. This will be faster than building a list via string concatenation. ---> <cfset LOCAL.Result = ArrayNew( 1 ) /> <!--- Convert the target list into an array for faster list iteration. ---> <cfset LOCAL.ListArray = ListToArray( ARGUMENTS.List, ARGUMENTS.Delimiters ) /> <!--- Convert our value list into struct. This will allow us to do super fast value look ups to see if we have a value requires deletion. We aren't going to bother converting this list to an array first (as we did above) because the likely scenario is that we won't have many values (and generally only one). ---> <cfset LOCAL.ValueLookup = StructNew() /> <!--- Loop over value list to create index. ---> <cfloop index="LOCAL.ValueItem" list="#ARGUMENTS.Value#" delimiters="#ARGUMENTS.Delimiters#"> <!--- Create index entry. ---> <cfset LOCAL.ValueLookup[ LOCAL.ValueItem ] = true /> </cfloop> <!--- Now that we have our index in place, it's time to start looping over the target list and looking for target values in our index. NOTE: Since our index is a struct, the lookups will NOT be case sensisitve. ---> <cfloop index="LOCAL.ValueIndex" from="1" to="#ArrayLen( LOCAL.ListArray )#" step="1"> <!--- Get a short hand to the current list value. ---> <cfset LOCAL.Value = LOCAL.ListArray[ LOCAL.ValueIndex ] /> <!--- Check to see if this value is in the index. ---> <cfif NOT StructKeyExists( LOCAL.ValueLookup, LOCAL.Value )> <!--- We are not deleting this value so add it to the taret array. ---> <cfset ArrayAppend( LOCAL.Result, LOCAL.Value ) /> </cfif> </cfloop> <!--- At this point, our target list has been trimmed and stored in the results array. Now, we have to convert the array back to a list. This poses a little bit of complication: we can only use one delimiter. Therefore, we might lose some meaningful delimiters. This has been done in the tradeoff for faster processing. ---> <cfreturn ArrayToList( LOCAL.Result, Left( ARGUMENTS.Delimiters, 1 ) ) /> </cffunction>
And now, just to run a quick test. Let's create create a multi-delimiter list of numbers and then delete the even values:
<!--- Create a multi-delimited list. ---> <cfset lstNumbers = "1,1,2,2:3:3,4,4:5:5,6,6:7:7,8,8:9:9" /> <cfset lstOddNumbers = ListDeleteValue( lstNumbers, "2,4,6,8", ",:" ) /> <!--- Output odd values. ---> #lstOddNumbers#
Running the above code, we get the following output:
Notice that we deleted all the even value. Notice also that our dual-delimited list was converted into a comma-delimited list. This is because when creating the resulting list, we select only the first available delimiter.
Want to use code from this post? Check out the license.