Coldfusion CFML可以有效地确定一个值是否存在于多个列表中的一个列表中

Coldfusion CFML可以有效地确定一个值是否存在于多个列表中的一个列表中,coldfusion,Coldfusion,这是我第一次发布到SO,这是一个非常有价值的资源 我试图确定代码列表中是否存在值状态代码,如果存在,请设置一个表示部门的新变量,即sales territory,no match=15。下面的代码可以工作,但我想提高我的技能并尽可能高效地完成这项工作。所以我的问题是,有没有更好的方法来达到同样的效果 <cfset state = "LA"> <cfset division = 15> <cfset position = 0> <cfset aState

这是我第一次发布到SO,这是一个非常有价值的资源

我试图确定代码列表中是否存在值状态代码,如果存在,请设置一个表示部门的新变量,即sales territory,no match=15。下面的代码可以工作,但我想提高我的技能并尽可能高效地完成这项工作。所以我的问题是,有没有更好的方法来达到同样的效果

<cfset state = "LA">
<cfset division = 15>
<cfset position = 0>

<cfset aStates = ArrayNew(1)>
<cfset aStates[1] = "MA,ME,NH,RI,VT">
<cfset aStates[2] = "CT,DE,NJ,NY,DE">
<cfset aStates[3] = "DC,MD,VA,WV">
<cfset aStates[4] = "TN">
<cfset aStates[5] = "NC,SC">
<cfset aStates[6] = "GA">
<cfset aStates[7] = "FL">
<cfset aStates[8] = "AL,KY,LA,MS">
<cfset aStates[9] = "IL,WI">
<cfset aStates[10] = "CO,MN,ND,SD,WY">
<cfset aStates[11] = "IN,OH,MI">
<cfset aStates[12] = "ID,OR,UT,WA">
<cfset aStates[13] = "AZ,HI,NV">
<cfset aStates[14] = "CA">

<cfset position = 0>
<cfloop array="#aStates#" index="lStates">
<cfset position = position+1>
<cfif ListFindNoCase(lStates,variables.state) NEQ 0>
<cfset division = position>
</cfif>
</cfloop>

<cfdump var="#aStates#" label="states array">
<cfoutput>State: #state#</cfoutput>
<cfoutput>Division: #division#</cfoutput>

对于当前的结构,您依赖于字符串比较,因此没有太多的改进空间

除非有特定原因必须硬编码值,否则更灵活的方法是将信息存储到数据库中。创建用于存储状态和分区的表,以及另一个用于存储关系的表:

States
StateID | Code | Name
   1    | ME   | Maine
   2    | GA   | Georgia
   3    | CA   | California
   4    | NH   | New Hampshire
...

Divisions
DivisionID | Name 
   1       | Sales Territory
...

DivisionStates
DivisionID | StateID
   1       |   1
   1       |   4
   6       |   2
  14       |   3
...
然后使用外部联接检索选定的状态和分区。假设状态总是包含一个有效的条目,那么沿着以下几行的内容未被测试:

SELECT s.Name AS StateName
       , CASE WHEN d.Name IS NULL THEN 'No Match' ELSE d.Name END AS DivisionName
FROM   States s 
         LEFT JOIN DivisionStates ds ON ds.StateID = s.StateID
         LEFT JOIN Divisions d ON d.DivisionID = ds.DivisionID
WHERE  s.Code = <cfqueryparam value="#state#" cfsqltype="cf_sql_varchar">

我不知道状态变量的来源,但我猜它可能是通过表单传递的。如果它当前是硬编码的,您可以通过查询states表并使用它填充列表来进一步简化。此外,考虑使用ID作为列表值,而不是状态代码。

< P>我考虑了一种方法,通过将状态放置到结构中;以州代码为键

    <cfscript>
        state = "LA";
        division = 15;

        states_divisions_map = {
            MA: 1, ME: 1, NH: 1, RI: 1, VT: 1,
            CT: 2, NJ: 2, NY: 2, DE: 2,
            DC: 3, MD: 3, VA: 3, WV: 3,
            TN: 4,
            NC: 5, SC: 5,
            GA: 6,
            FL: 7,
            AL: 8, KY: 8, LA: 8, MS: 8,
            IL: 9, WI: 9,
            CO: 10, MN: 10, ND: 10, SD: 10, WY: 10,
            IN: 11, OH: 11, MI: 11,
            ID: 12, OR: 12, UT: 12, WA: 12,
            AZ: 13, HI: 13, NV: 13,
            CA: 14
        };

        if(StructKeyExists (states_divisions_map, state) ){
            division = states_divisions_map[state];
        }

        writeDump(var: states_divisions_map, label: "States");
        writeOutput("State: #state#");
        writeOutput("Division: #division#");
    </cfscript>
这样,您可以快速检查状态,而无需所有循环

观察:在您的原始代码中,DE出现了两次。

ListContains类似于ListFind,但它会搜索包含您在其中任何位置查找的文本的列表元素。ListFind查找与字符串完全匹配的列表元素。通常,ListFind是更好的工具,但在本例中并非如此

CF确实有一个ArrayContains,但它不适用于此任务,它只是告诉您数组是否包含完全匹配的元素

因为您正在搜索唯一的双字符状态代码,所以这是该函数的完美用法

<cfset aStates = ArrayNew(1)>
<cfset aStates[1] = "MA,ME,NH,RI,VT">
<cfset aStates[2] = "CT,DE,NJ,NY,DE">
<cfset aStates[3] = "DC,MD,VA,WV">
<cfset aStates[4] = "TN">
<cfset aStates[5] = "NC,SC">
<cfset aStates[6] = "GA">
<cfset aStates[7] = "FL">
<cfset aStates[8] = "AL,KY,LA,MS">
<cfset aStates[9] = "IL,WI">
<cfset aStates[10] = "CO,MN,ND,SD,WY">
<cfset aStates[11] = "IN,OH,MI">
<cfset aStates[12] = "ID,OR,UT,WA">
<cfset aStates[13] = "AZ,HI,NV">
<cfset aStates[14] = "CA">
<cfset lstates = arraytolist(astates,"|")>
<cfset division = listcontainsnocase(lstates,"CO","|")>
<cfoutput>Division: #division#<br>
  State: #state#</cfoutput>
我们将分隔符设置为| pipe,但您可以使用除默认逗号以外的任何东西,因为您将其用作子分隔符

作为将来的参考,如果您使用的是CF9(我相信是CF9)或之后的版本,您可以使用数组速记来快速构建数组

<cfset aStates=["MA,ME,NH,RI,VT","CT,DE,NJ,NV,DE","DC,MD,VA,WV"]> ...
虽然这看起来只是风格上的差异,但它节省了很多时间

可以类似地创建结构

<cfset MyDogIs = {Size = "medium", Color = "Black", HasPaws = ["FL","FR","BL","BR"]}>

您可以将隐式数组和结构嵌套在另一个数组和结构中

谢谢大家的意见。我自己用我决定使用的代码来回答这个问题,因为它基于多个答案/注释提供的片段。我希望这是正确的方法,如果不是请建议

分区没有存储在数据库中,因为它们是一个多年来没有改变的set-it-and-forget-it映射,我想我应该保存一个对数据库的调用。如果它们可能发生变化,我会采用@leigh建议的表格方法

感谢@matt busche提出的改进循环、ucase和break建议,以及@cfqueryparam提出的简短数组建议

这是我要的。该代码实际上是cfc中处理表单提交的函数,但我只粘贴了相关部分

 <cfset form.division = 15>

     <cfset aDivisions = ["MA,ME,NH,RI,VT",
                          "CT,DE,NJ,NY,DE",
                          "DC,MD,VA,WV",
                          "TN",
                          "NC,SC",
                          "GA",
                          "FL",
                          "AL,KY,LA,MS",
                          "IL,WI",
                          "CO,MN,ND,SD,WY",
                          "IN,OH,MI",
                          "ID,OR,UT,WA",
                          "AZ,HI,NV",
                          "CA"]>

      <cfloop from="1" to="#ArrayLen(aDivisions)#" index="i">
        <cfif ListFind(aDivisions[i],Ucase(arguments.sForm.state)) NEQ 0>
        <cfset form.division = i>
        <cfbreak>
        </cfif>
      </cfloop>

您可以从cflib使用它,但我真的建议您在if语句中抛出一个,以便在找到循环后存在该循环。然后你可以杀掉你的柜台。您还可以在不使用nocase部分的情况下将状态大写,然后执行arrayFind。您可能需要在其他地方重新使用它,例如用于其他表单、报告、提交后验证值等。如果没有数据库,您将需要在多个位置对值进行硬编码,从而使其更容易出错,也更难维护。就我个人而言,我会使用数据库。在保证的情况下使用db查询没有任何问题。但是,如果列表仅在一个屏幕中使用,则没有多大区别。