Mohamad
Mohamad

Reputation: 35339

How do I remove duplicate values from a Coldfusion array?

I have a function that receives a string of tags. In order to save the tags individually, the function transforms the string into an array:

this.tags = listToArray(this.tags, ", ");

How do I remove duplicate values in the event that there are any?

Upvotes: 12

Views: 17174

Answers (10)

Jason Haritou
Jason Haritou

Reputation: 219

I like to use Java for this kind of task:

<cfset tags = "apples,oranges,bananas,pears,apples" />

<cfset tagsArray = createObject("java", "java.util.ArrayList").init(
createObject("java", "java.util.HashSet").init(ListToArray(tags))
) />

<cfdump var="#tags#" />
<cfdump var="#tagsArray#" />

Only problem is it takes case into account, so thinks "apples" & "APPLES" are different things (which technically yes, depending on your system may well be different). Way round that is to lower case everything in the list first. (NOTE: Added java.util.ArrayList function so that the array is identified & reusable by Adobe ColdFusion; otherwise functions like arraysort will throw an error.)

Upvotes: 21

dominikj111
dominikj111

Reputation: 375

I'm posting another neat solution I like.

arrayReduce(arrayWithDuplicates, function(resultArr, item) {
   if(!arrayFind(resultArr, item)){
      arrayAppend(resultArr, item);
   }
   return resultArr;
}, [])

ArrayReduce is available from ColdFusion 11.

But to answer better for the question, from the ColdFusion 10, we have ListRemoveDuplicates function available.

So the final code may looks like this:

this.tags = listToArray(listRemoveDuplicates(this.tags, ", "), ", ");

Upvotes: 0

linkary
linkary

Reputation: 123

Since you're really starting with a string/list that you're then converting to an array, you can pass the string through ListRemoveDuplicates before converting the to an array. ListRemoveDuplicates was introduced in Coldfusion 10; the input parameters are (list, delimiter=",", ignoreCase=FALSE).

this.tags = listToArray(listRemoveDuplicates(arrayInput,", ",TRUE));

If you were actually starting with an array, you would need to convert it to a list first, then back again after.

this.tags = listToArray(listRemoveDuplicates(arrayToList(arrayInput),", ",TRUE) );

Upvotes: 10

Ryan Guill
Ryan Guill

Reputation: 13886

Taking jason's answer just a little bit further, here is an arrayDistinct function.

function arrayDistinct (required array data) {
    var output = arrayNew(1);
    output.addAll(createObject("java", "java.util.HashSet").init(arguments.data));
    return output;
}

You can test it here: https://trycf.com/gist/62ff904d4500519e3144fc9564d2bce7/acf

Upvotes: 1

Russ
Russ

Reputation: 1951

In Coldfusion 10 or Railo 4, you could use Underscore.cfc's uniq() function:

_ = new Underscore();

uniqueArray = _.uniq(arrayWithDuplicates);

One advantage of uniq() is that it allows you to pass a transformation function, if necessary.

Note: I wrote Underscore.cfc

Upvotes: 2

da_didi
da_didi

Reputation: 842

Just put the array into a Struct and then copy it back to an array ;)

http://www.bennadel.com/blog/432-Using-ColdFusion-Structures-To-Remove-Duplicate-List-Values.htm

Upvotes: 0

invertedSpear
invertedSpear

Reputation: 11054

I just had to de-dup a very large list (5k+entries) and found a much faster way than using a loop. I feel the need to share.

  1. convert list to array (you already have array, so skip)<cfset thisArray = ListToArray(thisList)>
  2. Create a query with queryNew("") <cfset thisQuery = QueryNew("")>
  3. Add column to that query with the array from step1 <cfset temp = QueryAddColumn(thisQuery,"items","varChar",thisArray)>
  4. Query for Distinct values <cfquery name="qItems" dbtype="query">SELECT DISTINCT items FROM thisQuery</cfquery>
  5. convert result to list <cfset returnString = ValueList(qItems.items)>
  6. It's an easy step for you to convert this list back to an array

I wrote this into a function for easy use:

<cffunction name="deDupList" output="no" returntype="string">
    <cfargument name="thisList" required="yes">
    <cfargument name="thisDelimeter" required="yes" default=",">
    <cfset var loc = StructNew()>

    <cfset loc.thisArray = ListToArray(thisList,thisDelimeter)>
    <cfset loc.thisQuery = QueryNew("")>
    <cfset loc.temp = QueryAddColumn(loc.thisQuery,"items","varChar",loc.thisArray)>
    <cfquery name="qItems" dbtype="query">
        SELECT DISTINCT items FROM loc.thisQuery
    </cfquery>
    <cfset loc.returnString = ValueList(qItems.items)>
    <cfreturn loc.returnString>
</cffunction>

I bench-marked it against a few other methods and here are the results in milliseconds:
Looping over List checking for > 1 instance: 6265
Using Henry's struct method: 2969
The above method: 31
Jason's Method: 30

Upvotes: 1

Henry
Henry

Reputation: 32885

based on idea of Jason Haritou, but you can do it in pure CF using Struct! (keys matching will be case-insensitive)

this.tags = listToArray(this.tags, ", ");
var tmpStruct = {};

for (var t in this.tags)
    tmpStruct[t] = "";

return structKeyArray(tmpStruct);

However, for small lists, I prefer Antony's solution.

Upvotes: 2

Larry C. Lyons
Larry C. Lyons

Reputation: 403

There are a couple of UDF's on CFLib that do this, ArrayyDiff (http://www.cflib.org/udf/arrayDiff) and ArrayCompare (http://www.cflib.org/udf/arrayCompare).

hth, larry

Upvotes: -1

Antony
Antony

Reputation: 3781

An easy way to remove duplicates from a list is to convert the list to a struct first, and then conver the struct to an array. However if the order of items in the list is important this may not be appropriate as the elements in the struct will be sorted.

If the order of items is important you would need to build the array manually rather than using the listToArray feature.

<!--- CF9 --->
<cfset tags = "apples,oranges,bananas,pears,APPLES" />
<cfset tagArray = arrayNew(1) />

<cfloop list="#tags#" index="tag" delimiters=",">
    <cfif not ArrayFindNoCase(tagArray,tag)>
        <cfset arrayAppend(tagArray, tag) />
    </cfif>
</cfloop>

Upvotes: 12

Related Questions