如何从数组Coldfusion中删除重复的值

时间:2018-09-21 22:49:32

标签: coldfusion

如何在Coldfusion中从数组列表中删除重复的值。

当前,我有以下内容显示用户去医院的原因:

<cfset ReasonforVisit = "#HospitalVisits2.Reason_For_Visit#">
    <cfset counter1 = 1>
    <cfif len(ReasonforVisit)>
      <cfloop query="HospitalVisits2">  
        <cfoutput>
            &nbsp;#HospitalVisits2.Reason_For_Visit#<cfif counter1 LT HospitalVisits2.recordcount>,</cfif>  <cfset counter1++>
        </cfoutput>
      </cfloop></cfif>

这种方法的问题没有考虑到列中的重复。

我已经完成了以下操作,以将列的结果存储在数组中,并检查是否重复,但是不起作用。我在做什么错了?

<cfset ReasonforVisit = "#HospitalVisits2.Reason_For_Visit#">
<cfset ReasonList = ArrayNew(ReasonforVisit)>

    <cfloop index="i" from="1" to="#arraylen(ReasonList)#">
            <cfset currReasonList = ReasonList[i]>
            <cfset nextReasonList = ReasonList[i+1]>
            <cfif currReasonList NEQ nextReasonList>
                <cfset newReasonList = ArrayNew(newcurrReasonList)>
            <cfelse>
        </cfif>
    </cfloop>
<cfoutput>#newReasonList#</cfoutput>

3 个答案:

答案 0 :(得分:2)

这是一个Lucee功能请求。也许您可以使用此页面上的示例:

https://luceeserver.atlassian.net/browse/LDEV-442

<cfscript>
s = "a string";
i = 42;
f = pi();
st1 = {key1="value1"};
st1bis = {key1="value1"};
st1ref = st1;
st2 = {key2="value2"};
a1 = [1];
a1bis = [1];
a1ref = a1;
a2 = [2];

base = [s,i,f,st1,st1bis,st1ref,st2,a1,a1bis,a1ref,a2,s,i,f,st1,st1bis,st1ref,st2,a1,a1bis,a1ref,a2];

function arrayRemoveDuplicates(array){
    return array.reduce(function(deduped, el){
        return deduped.find(el) ? deduped : deduped.append(el);
    }, []);
}

writeDump(var=base, label="original")
writeDump(var=arrayRemoveDuplicates(base), label="deduped")
</cfscript>

答案 1 :(得分:2)

我可能会误解您真正想要的东西,如果是,我将更改答案。根据显示的内容,您不想从数组中删除值。您要输出以逗号分隔的Reason_For_Visit列表,其中删除了所有重复项,对吗?

ColdFusion查询对象非常易于使用,并且自从该语言诞生之初就一直是CF最强大的功能之一。

ColdFusion查询对象已经具有许多数组和结构的属性,并且可以将那些复杂数据类型的许多相同功能和行为应用于cfquery对象。

您要的内容基本上可以在一行中完成(包含一些功能)。

newReasonList = listChangeDelims( 
    listRemoveDuplicates( 
        valuelist(HospitalVisits2.Reason_For_Visit, "|")
        ,"|"
        ,true 
    )
    ,", "
    ,"|"
    ,false 
) ;

我知道这显示为多行,但是我以这种方式格式化以使其更清晰,因为我讨厌在页面上滚动。 :-)

看看我们在做什么:

我们的查询对象是Reason_For_Visit查询的HospitalVisits2列。 valueList()是用于处理查询的最古老的CF函数之一。它将本质上从查询列(HospitalVisits2.Reason_For_Visit)中获取值,并将类似于数组的内容转换为该列(https://cfdocs.org/valuelist)的定界值列表。它可以使用可选的delimiter参数,该参数允许我们更改列表的定界符。我选择将,的默认值更改为|字符,这样,如果我们的任何项目都有逗号,以后就不会在列表中无意间解释它。

现在我们有了一个|分隔的Reason_For_Visit列表,我们可以在该列表上使用列表函数来消除重复对象。该函数非常聪明地命名为listRemoveDuplicates()https://cfdocs.org/listremoveduplicates)。它可以包含两个可选参数:列表的已定义定界符,以及是否忽略列表中字符串大小写的布尔值。我们给它一个在valueList()中设置的定界符,并告诉它忽略重复的情况,并且我们有一个重复数据删除列表。

现在,我们只需要更改字符串中的定界符即可,使它们不再|。幸运的是,Coldfusion还有另一个巧妙命名的列表函数listChangeDelims()https://cfdocs.org/listchangedelims)。此函数接受我们的列表和所需的新定界符的参数。默认为逗号。它还需要两个可选参数。我们可以指定当前的分隔符(|),然后告诉我们要包含空值。在这里,我们可能不希望这些容器为空。这样可以给我们一个像Thing1, Thing2,,Thing4这样的列表。

valueList()太旧了,可以在任何版本的ColdFusion甚至是半兼容的CFML解析器中使用。 listRemoveDuplicates()listChangeDelims()已添加到CF10中,并且在Lucee 4.5+中可用。显然,TryCF指示它仍将在Railo 4.2中运行。如果您使用的是旧版本,则删除这些重复项会比较棘手,并且可能会更慢。

我在https://trycf.com/gist/8e3107959cc051dd264b850bbeae88e3/acf?theme=monokai创建了一个示例。您还可以看到,这些函数将处理一个空查询,一个元素查询和一个空元素查询。您不会收到流浪逗号。

还有其他方法可以做到这一点,但我不确定它如何在非常大的列表中扩展,但这很快就会起作用。如果您需要遍历多个患者以输出其原因,则可以很容易地修改此方法。对于较新版本的CF,可以使用闭包和其他东西来使其更快一些。

================================================ =

编辑:针对CFMX进行了修改

https://trycf.com/gist/91bc66f0fd0b7597e7a4652e9d255e41/acf?theme=monokai

我应该注意,我目前尚无法测试像CFMX一样古老的产品,因此我的内存不足。我不记得是否可以在CFMX的valueList()structKeyList()中设置定界符。可以将其删除,您仍然会得到一个逗号分隔的列表。逗号后只是没有多余的空间。您必须进行测试。

从本质上讲,您可以循环查询并构建一个结构(本质上不会插入重复的键),,但是当我尝试输出结构键列表时却看到一个逗号开头,这需要附加正在处理以删除该逗号。因此,我认为更简洁的方法可能能够遍历值列表,而不是遍历查询。我知道循环遍历列表要比循环遍历查询慢,但是我不确定这对您的规模有多大影响。我去了:

<!--- create the struct --->
<cfset newReasonStruct = StructNew() />

<!--- CF Structs will overwrite duplicate keys. ---> 
<!--- NOTE: I can't remember if delimiters were valid arguments for valuelist() in CFMX --->
<cfloop index="VisitReason" list="#valuelist(HospitalVisits2.Reason_For_Visit, "|")#" delimiters="|">
    <cfif len(trim(Reason_For_Visit))> <!--- Don't add empties. --->
        <cfset newReasonStruct[VisitReason] = "" > 
    </cfif>
</cfloop>

<!--- Set a variable for our new list. --->
<cfset newReasonList = StructKeyList(newReasonStruct,", ") >

This is our main query (with valuelist()): <cfoutput>#newReasonList#</cfoutput>

编辑2:参见上面的我的删除线。我没想前导逗号是由查询循环中的空元素引起的。这导致“键”值为空字符串,然后将其排序到我的输出字符串的开头。 valueList()列表循环方法正在过滤掉该空元素,但查询循环却没有。因此,可以通过更改查询循环并在结构为空的情况下不向结构中添加“键”来完成修剪前导逗号的额外处理。但是,这从最后的一组cfif/right()函数变为查询中每个循环的len(trim())函数。同样,根据您的数据,一个功能可能比另一个功能更高效。尽管这有点像微优化。

<!--- CF Structs will overwrite duplicate keys. ---> 
<cfloop query="HospitalVisits2">
    <cfif len(trim(Reason_For_Visit))> <!--- Don't add empties. --->
      <cfset newReasonStruct[Reason_For_Visit] = "" > 
    </cfif>
</cfloop>

https://trycf.com/gist/169e9e0a592bf88aac462cc3a6e0d2c6/acf?theme=monokai

答案 2 :(得分:0)

更快,更现代:

public array function arrayUnique(required array sourceArray){
  var source = arguments.sourceArray;
  return source.filter((item, index) => source.find(arguments.item) == arguments.index);
}