Merge two objects in Coldfusion(using openBD) issue

553 views Asked by At

I have the following code block:

<cfset docs1 = StructNew() />
<cfset docs1['content'] = "I am" />
<cfset docs1['ida'] = "23" />
<cfset docs1['solrid'] = "23_solrid" />
<cfset docs1['title'] = "Aaaaa" />

<cfset docs2 = StructNew() />
<cfset docs2['content'] = "the most" />
<cfset docs2['ida'] = "1" />
<cfset docs2['solrid'] = "1_solrid" />
<cfset docs2['title'] = "Bbbb" />

<cfset docs3 = StructNew() />
<cfset docs3['content'] = "crap coder" />
<cfset docs3['ida'] = "7" />
<cfset docs3['solrid'] = "7_solrid" />
<cfset docs3['title'] = "Cccc" />

<cfset docs4 = StructNew() />
<cfset docs4['content'] = "in the whole universe!" />
<cfset docs4['ida'] = "39" />
<cfset docs4['solrid'] = "39_solrid" />
<cfset docs4['title'] = "Dddd" />

<cfscript> 
    doca = ArrayNew(); 
    ArrayAppend(doca, docs1); 
    ArrayAppend(doca, docs2); 
    ArrayAppend(doca, docs3); 
    ArrayAppend(doca, docs4);  
</cfscript>  

<cfset forsolr1 = StructNew()>
<cfset forsolr1['docs'] = doca>
<cfset forext1 = SerializeJson(forsolr1)>

<cfdump var="#forsolr1#" label="Struct1">
<cfoutput>
    #forext1#
</cfoutput>
<br /><br /><br />


<cfset docsh = StructNew() />
<cfset docsh['23_solrid'] = "I need" />
<cfset docsh['1_solrid'] = "to read" />
<cfset docsh['7_solrid'] = "a lot of" />
<cfset docsh['39_solrid'] = "programming books!" />

<cfset i = 1>
<cfset solrid = "solrid">
<cfset result = ArrayNew(1)>

<cfloop collection="#docsh#" item="key" >
  <cfset innerstruct = StructNew()>
  <cfset innerstruct['solrid'] = #key#>
  <cfset innerstruct['dochighlighted'] = #docsh[key]#>
  <cfset result[i] = innerstruct>
  <cfset i = i + 1>
</cfloop>

<cfset forsolr = StructNew()>
<cfset forsolr['docs'] = result>
<cfset forext = SerializeJson(forsolr)>

<cfdump var="#forsolr#" label="Struct2">
<cfoutput>
    #forext#
</cfoutput>

The result are two objects(struct of array of structs):

First object

{
   "docs":[
      {
         "content":"I am",
         "ida":"23",
         "solrid":"23_solrid",
         "title":"Aaaaa"
      },
      {
         "content":"the most",
         "ida":"1",
         "solrid":"1_solrid",
         "title":"Bbbb"
      },
      {
         "content":"crap coder",
         "ida":"7",
         "solrid":"7_solrid",
         "title":"Cccc"
      },
      {
         "content":"in the whole universe!",
         "ida":"39",
         "solrid":"39_solrid",
         "title":"Dddd"
      }
   ]
}

Second object

{
   "docs":[
      {
         "solrid":"23_solrid",
         "dochighlighted":"I need"
      },
      {
         "solrid":"1_solrid",
         "dochighlighted":"to read"
      },
      {
         "solrid":"7_solrid",
         "dochighlighted":"a lot of"
      },
      {
         "solrid":"39_solrid",
         "dochighlighted":"programming books!"
      }
   ]
}

Is it possible to merge these two objects into one in the form of(copy second to the first):

{
   "docs":[
      {
         "content":"I am",
         "ida":"23",
         "solrid":"23_solrid",
         "title":"Aaaaa",
         "dochighlighted":"I need"
      },
      {
         "content":"the most",
         "ida":"1",
         "solrid":"1_solrid",
         "title":"Bbbb",
         "dochighlighted":"to read"
      },
      {
         "content":"crap coder",
         "ida":"7",
         "solrid":"7_solrid",
         "title":"Cccc",
         "dochighlighted":"a lot of"
      },
      {
         "content":"in the whole universe!",
         "ida":"39",
         "solrid":"39_solrid",
         "title":"Dddd",
         "dochighlighted":"programming books!"
      }
   ]
}

I had tried structAppend function with no result. This function cannot append deep nested elements. Is it possible to retain order? The reason for that is that the above objects are search engine's results, so it is very important to keep the original order. Is it possible to merge two objects by key reference? The reason that I am asking is that the first object is coming out from the search engine in a specific order (notice the solrid) but the second object may not. Example:

 *First object*

{
   "docs":[
      {
         "content":"I am",
         "ida":"23",
         "solrid":"23_solrid",
         "title":"Aaaaa"
      },
      {
         "content":"the most",
         "ida":"1",
         "solrid":"1_solrid",
         "title":"Bbbb"
      },
      {
         "content":"crap coder",
         "ida":"7",
         "solrid":"7_solrid",
         "title":"Cccc"
      },
      {
         "content":"in the whole universe!",
         "ida":"39",
         "solrid":"39_solrid",
         "title":"Dddd"
      }
   ]
}

 *Second object*

{
   "docs":[
      {
         "solrid":"39_solrid",
         "dochighlighted":"programming books!"
      },
      {

         "solrid":"7_solrid",
         "dochighlighted":"a lot of"
      },
      {
         "solrid":"1_solrid",
         "dochighlighted":"to read"
      },
      {
         "solrid":"23_solrid",
         "dochighlighted":"I need"
      }
   ]
}

Is it possible to achieve the same -as above- merging result?

{
   "docs":[
      {
         "content":"I am",
         "ida":"23",
         "solrid":"23_solrid",
         "title":"Aaaaa",
         "dochighlighted":"I need"
      },
      {
         "content":"the most",
         "ida":"1",
         "solrid":"1_solrid",
         "title":"Bbbb",
         "dochighlighted":"to read"
      },
      {
         "content":"crap coder",
         "ida":"7",
         "solrid":"7_solrid",
         "title":"Cccc",
         "dochighlighted":"a lot of"
      },
      {
         "content":"in the whole universe!",
         "ida":"39",
         "solrid":"39_solrid",
         "title":"Dddd",
         "dochighlighted":"programming books!"
      }
   ]
}

Many many thanks,

With honour,

Tom

Greece

1

There are 1 answers

4
Tomalak On BEST ANSWER

This should get you pretty far:

<cffunction name="ArrayOfStructsMerge" returntype="array" access="public" output="yes">
  <cfargument name="left"  type="array"  required="yes">
  <cfargument name="right" type="array"  required="yes">
  <cfargument name="id"    type="string" required="yes">

  <cfset var result = Duplicate(arguments.left)>
  <cfset var element = "">
  <cfset var key = "">
  <cfset var currentId = "">
  <cfset var lookup = StructNew()>

  <cfloop array="#result#" index="element">
    <cfif IsStruct(element) and StructKeyExists(element, arguments.id)>
      <cfset currentId = element[arguments.id]>
      <cfset lookup[currentId] = element>
    </cfif>
  </cfloop>

  <cfloop array="#arguments.right#" index="element">
    <cfif IsStruct(element) and StructKeyExists(element, arguments.id)>
      <cfset currentId = element[arguments.id]>
      <cfif StructKeyExists(lookup, currentId)>
        <cfloop collection="#element#" item="key">
          <cfset lookup[currentId][key] = Duplicate(element[key])>
        </cfloop>
      <cfelse>
        <cfset ArrayAppend(result, Duplicate(element))>
      </cfif>
    </cfif>
  </cfloop>

  <cfreturn result>
</cffunction>

It operates non-destructively (without modifying the original objects). Call it like this:

<cfset result = ArrayOfStructsMerge(obj1.docs, obj2.docs, "solrid")>
<cfdump var="#result#">

<cfset a1 = [{id: 1, a: "a1"}, {id: 2, a: "a2"}, {id: 4, d: "d4"}]>
<cfset a2 = [{id: 1, a: "a1-new"}, {id: 2, b: "b2"}, {id: 3, c: "c3"}]>
<cfset a3 = ArrayOfStructsMerge(a1, a2, "id")>

Result of ArrayOfStructsMerge()