Creating a function in Coldfusion to dynamically convert a multidimensional array to a query object - multidimensional-array

The problem I'm running into is the API I'm using for this particular project uses periods in the keys which is not allowed in query column names. So I need to figure out a way to rename the array keys to remove the periods (or replace them with an underscore). Here is the code I have so far, which is working, but throws an error when hitting the "accumulator.addRow" line. The error being that periods aren't allowed in column names.
<cfset varResults = '[{"roW_NUMBER":1,"membership.Individual.LocalID":999,"membership.Individual.FirstName":"AA","membership.Individual.LastName":"AA","membership.Individual.Company":null,"membership.Individual.Work_Address_Line1":"aaaa","membership.Individual.Work_Address_Line2":null,"membership.Individual.Work_Address_City":"Alexandria","membership.Individual.Work_Address_State":"VA","membership.Individual.Work_Address_PostalCode":"9999-2518","membership.Individual.Work_Address_CountryName":"UNITED STATES","membership.Individual.EmailAddress":"AA.AA#gmail.com","membership.Type.Name":"Regular","membership.JoinDate":"2009-05-01T00:00:00","membership.ExpirationDate":"2023-04-30T00:00:00","membership.Individual.FellowElectDate__c":null,"chapter.Name":"DDDD","id":"0e04bc98-0074-cc58-bf6a-0b438d800e7d"},{"roW_NUMBER":2,"membership.Individual.LocalID":9998,"membership.Individual.FirstName":"AA","membership.Individual.LastName":"AA","membership.Individual.Company":null,"membership.Individual.Work_Address_Line1":"ASASASAAASAS","membership.Individual.Work_Address_Line2":null,"membership.Individual.Work_Address_City":"Washington","membership.Individual.Work_Address_State":"DC","membership.Individual.Work_Address_PostalCode":"9999","membership.Individual.Work_Address_CountryName":"UNITED STATES","membership.Individual.EmailAddress":"ASASASA#gmail.com","membership.Type.Name":"Regular","membership.JoinDate":"2020-01-01T00:00:00","membership.ExpirationDate":"2022-12-31T00:00:00","membership.Individual.FellowElectDate__c":null,"chapter.Name":"DDDD","id":"0e04bc98-0074-c1ae-f171-0b438d800e7c"}]'>
<cfscript>
function arrayToQuery(varChapterRoster) {
return varChapterRoster.reduce(function(accumulator, element) {
element.each(function(key) {
if (!accumulator.keyExists(replace(key,".","_","all"))) {
accumulator.addColumn(replace(key,".","_","all"), []);
}
});
//writeDump(element);
accumulator.addRow(element);
return accumulator;
}, QueryNew(""));
}
// writeDump(varChapterRoster);
writeDump(arrayToQuery(varChapterRoster));
</cfscript>
Specific error: Column names must be valid variable names. They must start with a letter and can only include letters, numbers, and underscores.
I got past the "keyexists" and "addcolumn" lines by using the replace function, but replace won't work on "element" (throws an error) on the addRow line.
Changing tactics, here is a CFFiddle with some dummy data in it of the method I'm trying now. I think it is more straight forward, but getting another error that I bet is related to the periods again.

<cfset varResults = '[{"roW_NUMBER":1,"membership.Individual.LocalID":999,"membership.Individual.FirstName":"AA","membership.Individual.LastName":"AA","membership.Individual.Company":null,"membership.Individual.Work_Address_Line1":"aaaa","membership.Individual.Work_Address_Line2":null,"membership.Individual.Work_Address_City":"Alexandria","membership.Individual.Work_Address_State":"VA","membership.Individual.Work_Address_PostalCode":"9999-2518","membership.Individual.Work_Address_CountryName":"UNITED STATES","membership.Individual.EmailAddress":"AA.AA#gmail.com","membership.Type.Name":"Regular","membership.JoinDate":"2009-05-01T00:00:00","membership.ExpirationDate":"2023-04-30T00:00:00","membership.Individual.FellowElectDate__c":null,"chapter.Name":"DDDD","id":"0e04bc98-0074-cc58-bf6a-0b438d800e7d"},{"roW_NUMBER":2,"membership.Individual.LocalID":9998,"membership.Individual.FirstName":"AA","membership.Individual.LastName":"AA","membership.Individual.Company":null,"membership.Individual.Work_Address_Line1":"ASASASAAASAS","membership.Individual.Work_Address_Line2":null,"membership.Individual.Work_Address_City":"Washington","membership.Individual.Work_Address_State":"DC","membership.Individual.Work_Address_PostalCode":"9999","membership.Individual.Work_Address_CountryName":"UNITED STATES","membership.Individual.EmailAddress":"ASASASA#gmail.com","membership.Type.Name":"Regular","membership.JoinDate":"2020-01-01T00:00:00","membership.ExpirationDate":"2022-12-31T00:00:00","membership.Individual.FellowElectDate__c":null,"chapter.Name":"DDDD","id":"0e04bc98-0074-c1ae-f171-0b438d800e7c"}]'>
<cfset varResults = replace(varResults,":null",':""',"All")>
<cfset varResults = replace(varResults,"T00:00:00","","All")>
<cfset varResults = deserializeJSON(varResults)>
Answer based on original post. Demo
<cfscript>
function arrayToQuery(varChapterRoster) {
if(varChapterRoster.len() == 0){
return QueryNew('');
}
return varChapterRoster.reduce(function(accumulator, element) {
Row = QueryAddRow(accumulator);
element.each(function(key) {
QuerySetCell( accumulator , replace(key,".","_","all") , element[key] , Row );
});
return accumulator;
}, QueryNew(replace(StructKeyList(varResults[1]),".","_","all")));
}
// writeDump(varChapterRoster);
writeDump(arrayToQuery(varResults));
</cfscript>
Answer based on your cffiddle. Demo
<cfif NOT ArrayLen(varResults)>
<cfthrow message="No data" />
</cfif>
<cfset Q = QueryNew( replace(StructKeyList(varResults[1]),".","_","all") ) />
<cfloop index="item" array="#varResults#">
<cfset Row = QueryAddRow(Q) />
<cfloop item="ColName" collection="#item#" >
<cfset QuerySetCell( Q , replace(ColName,".","_","all") , item[ColName] , Row ) />
</cfloop>
</cfloop>
<cfdump var=#Q# />

Related

How can I create xquery using xml response

The below mentioned is actual xquery.
declare variable $rankCode_DL3UJ :=("CPT","FO","FM");
declare variable $rankCode_DL4UJ :=("CRFO","CL");
declare variable $type :=("DOM","ISH");
declare function local:getboardingPriorityCode($flightType as element(), $rankCode as element()) as xs:string
{
if(data($flightType) = $type) then
'DHD'
else
if(data($rankCode) = $rankCode_DL3UJ) then
'DL3UJ'
else if(data($rankCode) = $rankCode_DL4UJ) then
'DL4UJ'
else
'DL5UJ'
};
declare function local:getPreferredClass($flightType as element(flightCategory:Code)) as xs:string
{
if(data($flightType) = $type) then
'Y'
else
'J'
};
declare function xf:createDeadheadReservationRQ-to- PassengerDetailsRQ($createDeadheadReservationRQ1 as element(ns1:CreateDeadheadReservationRQ),$CreateDeadHeadReservationAdapterSelect RS as element(ns3:RefTickettypeCollection),
$airBookRS as element(flightCategory:GetFlightCategoryRS))
as element(ns2:PassengerDetailsRQ) {
<ns2:PassengerDetailsRQ Version = "2.0.0">
<ns2:SpecialReqDetails>
<ns2:AddRemarkRQ>
<ns2:RemarkInfo>
<ns2:Remark Type = "General">
<ns2:Text>{ data($createDeadheadReservationRQ1/ns1:SpecialRequestDetails/ns1:Remark) }</ns2:Text>
</ns2:Remark>
</ns2:RemarkInfo>
</ns2:AddRemarkRQ>
<ns2:SpecialServiceRQ>
<ns2:SpecialServiceInfo>
<ns2:SecureFlight SegmentNumber = "A" SSR_Code = "DOCS">
<ns2:PersonName DateOfBirth = "{ data($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:DateOfBirth) }"
Gender = "{ data($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:Gender) }"
NameNumber = "1.1">
<ns2:GivenName>{ data($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:PassengerName/ns1:GivenName) }</ns2:GivenName>
<ns2:Surname>{ data($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:PassengerName/ns1:Surname) }</ns2:Surname>
</ns2:PersonName>
<ns2:VendorPrefs>
<ns2:Airline Hosted = "true"/>
</ns2:VendorPrefs>
</ns2:SecureFlight>
<ns2:Service SSR_Code = "{ data($createDeadheadReservationRQ1/ns1:SpecialRequestDetails/ns1:RankBasedSSR) }">
<ns2:VendorPrefs>
<ns2:Airline Hosted = "true"/>
</ns2:VendorPrefs>
</ns2:Service>
</ns2:SpecialServiceInfo>
</ns2:SpecialServiceRQ>
</ns2:SpecialReqDetails>
<ns2:TravelItineraryAddInfoRQ>
<ns2:AgencyInfo>
<ns2:Ticketing TicketType = "{fn:concat('7PS-', local:getboardingPriorityCode($airBookRS/flightCategory:ServiceType/flightCategory:Code, $createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:RankCode), local:getPreferredClass($airBookRS/flightCategory:ServiceType/flightCategory:Code))}"/>
</ns2:AgencyInfo>
<ns2:CustomerInfo>
<ns2:PersonName>
<ns2:GivenName>{
xs:string(fn:concat($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:PassengerName/ns1:GivenName,' ',
$createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:PassengerName/ns1:Title))
}</ns2:GivenName>
<ns2:Surname>{ xs:string($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:PassengerName/ns1:Surname) }</ns2:Surname>
</ns2:PersonName>
</ns2:CustomerInfo>
</ns2:TravelItineraryAddInfoRQ>
</ns2:PassengerDetailsRQ>
};
declare variable $createDeadheadReservationRQ1 as element(ns1:CreateDeadheadReservationRQ) external;
declare variable $airBookRS as element(flightCategory:GetFlightCategoryRS) external;
declare variable $CreateDeadHeadReservationAdapterSelectRS as element(ns3:RefTickettypeCollection) external;
xf:createDeadheadReservationRQ-to- PassengerDetailsRQ($createDeadheadReservationRQ1, $airBookRS, $CreateDeadHeadReservationAdapterSelectRS)
So We have to do the below changes.
for that we have created the below mentioned table in DB, using JCA adapter we have received the response and created the xml response creating business service of JCA adapter in OSB.
condition: In query we need to fetch OTHER always so query will be as follows and if we get 2 results then pick one that is not “other” like pick CPT if it returned in result of select query and if we get 1 result then it will be always “Other”.
Select *from <Table> where Code_name in (‘OTHER’, 'value received in request');
xml request and response of business service JCA adapter:
Request:
<cre:CreateDeadHeadReservationAdapterSelect_CODE_NAMEInputParameters>
<cre:CODE_NAME>CPT</cre:CODE_NAME>
</cre:CreateDeadHeadReservationAdapterSelect_CODE_NAMEInputParameters>
Response:
<cre:RefTickettypeCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema- instance" xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cre="http://xmlns.oracle.com/pcbpel/adapter/db/top/CreateDeadHeadReservati onAdapter">
<cre:RefTickettype>
<cre:codeName>CPT</cre:codeName>
<cre:codeType>Rank</cre:codeType>
<cre:priorityCode>DL3UJ</cre:priorityCode>
<cre:preferredClass>J</cre:preferredClass>
</cre:RefTickettype>
<cre:RefTickettype>
<cre:codeName>OTHER</cre:codeName>
<cre:codeType>Other</cre:codeType>
<cre:priorityCode>DL7UJ</cre:priorityCode>
<cre:preferredClass>J</cre:preferredClass>
</cre:RefTickettype></cre:RefTickettypeCollection>
So using the above response could you pls help me to create the xquery logic.
I'm not sure I understand all of your question. But, it sounds like when two RefTickettype elements are returned, you want to select the one where codeName is not OTHER?
The following query checks to see how many RefTickettype elements are in the result. If there is only one, it is returned. If there is more than one, it will return all where codeName is not OTHER.
declare namespace cre="http://xmlns.oracle.com/pcbpel/adapter/db/top/CreateDeadHeadReservationAdapter";
let $response :=
<cre:RefTickettypeCollection>
<cre:RefTickettype>
<cre:codeName>CPT</cre:codeName>
<cre:codeType>Rank</cre:codeType>
<cre:priorityCode>DL3UJ</cre:priorityCode>
<cre:preferredClass>J</cre:preferredClass>
</cre:RefTickettype>
<cre:RefTickettype>
<cre:codeName>OTHER</cre:codeName>
<cre:codeType>Other</cre:codeType>
<cre:priorityCode>DL7UJ</cre:priorityCode>
<cre:preferredClass>J</cre:preferredClass>
</cre:RefTickettype>
</cre:RefTickettypeCollection>
return
if (count($response/cre:RefTickettype) eq 1) then
$response/cre:RefTickettype
else
$response/cre:RefTickettype[cre:codeName ne "OTHER"]

XPages: convert DateTime value to string using browser's locale

A similar question to a previous one I asked, but the difference being that this not for direct rendering from an underlying field - it's instead part of a some SSJS.
This is for a view column which displays the result of a SSJS function, which returns HTML that gets rendered. This HTML includes a date from a DateTime field, which gets converted to text using #Text. The problem I have with this is, #Text converts dates using the locale settings of the server, not the browser.
Is there an alternative to #Text(dateValue,"D0S0") that's browser locale aware?
The most "XPagey" way to do this is to use a date/time converter. For example (using a stand-in for the computed value):
<xp:viewColumn columnName="">
<xp:this.value><![CDATA[#{javascript:
new java.util.Date()
}]]></xp:this.value>
<xp:this.converter>
<xp:convertDateTime type="both"/>
</xp:this.converter>
</xp:viewColumn>
That "convertDateTime", with its built-in formats, will respect the browser's provided locale. If you set the option in the Xsp Properties to use the browser's time zone and "Round trip", it should also respect the user's time zone.
I've managed to get round this by using DateFormat.getDateInstance. The only problem with this is it doesn't return a short date in the same format as the XPage date converter (no leading zeros and a 2-figure year). I've got round this though with some fiddling around with the string after.
Here's the full function:
function returnLocalShortDate(ndtDate) {
// Receives NotesDateTime object, Java date or string; returns localised date string in XPages short date format
importPackage(java.text);
if (#IsText(ndtDate)) { // string
var jsDate = #TextToTime(ndtDate);
} else if (ndtDate instanceof Date) { // Java date
var jsDate:Date = ndtDate;
} else if (#IsTime(ndtDate)) { // Notes date/time
var jsDate:Date = ndtDate[0].toJavaDate();
} else {
return("");
}
var strDate:String = java.text.DateFormat.getDateInstance(DateFormat.SHORT, context.getLocale()).format(jsDate);
var strYear = jsDate.getFullYear();
var strDateArray = strDate.split("/");
strDate = ('0' + strDateArray[0]).slice(-2) + '/' + ('0' + strDateArray[1]).slice(-2) + '/' + strYear;
return(strDate);
}
Actually, if you know the format you want, rather than what the user might want via their browser settings, you should use the SimpleDateFormatter class. You can supply the format in accordance with whatever pattern you want from the javadocs for that class. If you supply the NotesDocument object and the field name, this returns the date in dd-MMM-yyyy format.
function getFormattedDate ( doc:NotesDocument, fieldName:String ) {
importPackage(java.text);
var dateFormatter:java.text.SimpleDateFormat = new SimpleDateFormat("dd-MMM-yyyy");
var d:Date = new Date(#Today());
if ( doc.hasItem (fieldName) ) {
var valueVector:java.util.Vector = doc.getItemValueDateTimeArray(fieldName);
var iterator = valueVector.iterator();
while (iterator.hasNext()) {
var itemvalue = iterator.next();
if ((typeof(itemvalue)).endsWith("DateTime")) {
d = new Date(itemvalue.toJavaDate());
return dateFormatter.format(d);
}
}
} else {
return fieldName + " is not on the document"
}
}
I owe credit to Declan Lynch's blog entry on date formatting, which takes a little debugging because SSJS returns the date value as an Vector now.

How to collect values for multi-valued queryparam in apigee?

For a request with a multi-valued queryparam like: https://test.apigee.net?storeIds=abc&storeIds=xyz, how can someone setup an extract variable policy so that there will be a storeIds array like: storeIds=["abc","xyz"]?
Update #1:
Using the following in javascript for Apigee:
var arrayOfStoreIds = [];
for (i = 0; i < context.proxyRequest.queryParams['storeIds'].length; i++) {
arrayOfStoreIds.push(context.proxyRequest.queryParams['storeIds'][i]);
}
Yields an error:
`Execution of script failed with error:
Javascript runtime error:
"TypeError: Cannot find default value for object ... at line ##"`
The line # referenced points to the 1st line of the for loop
Update #2:
Incorrect documentation at http://apigee.com/docs/api-services/reference/javascript-object-model:
context.proxyRequest.queryParams['city'].length; // == 2
Correct syntax at https://github.com/ap-andrew/DevGuide/blob/master/javascript_new.html#L141
context.proxyRequest.queryParams['city'].length(); // == 2
So with this context.proxyRequest.queryParams['storeIds'].length() it works! At least in javascript ... I still don't know how to do this via an extract variable policy...
Truth be told, there is no spec on using duplicate query parameters. However, you can clearly see there's an issue when trying to retrieve them; you'd encounter this in php using $_GET, in JavaScript, Node.js, et al.
I'd suggest if you can, do something using pipe or comma separation: storeIds=abc,xyz. Then to extract query parameters using an ExtractVariables policy, you would do something like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<ExtractVariables async="false" continueOnError="true" enabled="true" name="productsVariables">
<DisplayName>Extract</DisplayName>
<URIPath>
<QueryParam name="storeIds">
<Pattern ignoreCase="true">{storeIdsArray}</Pattern>
</QueryParam>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</ExtractVariables>
In your javascript, you could then do:
try {
var storeIdsArray = context.getVariable('storeIdsArray').split(',');
storeIdsArray.forEach(function(storeId) {
// do something with storeId
}
}
catch (e) {
// storeIdsArray wasn't defined or not an array or something else went wrong
}
In raw CGI you can split multiple values with the same queryparam on NULL, but in Apigee, by the time it gets to the Message Processor, the queryparam only contains the first value. So the only way to do this is brute force using the full message.querystring.
If I sent in:
testme?myQueryParam=Y&myQueryParam=X
I have to do a double split using JavaScript and loop through each queryparam doing something like this:
var text = "";
var myQueryParams = context.getVariable('message.querystring');
var myQueryParamArray = myQueryParams.split(/\&/);
for (i = 0; i < myQueryParamArray.length; i++) {
var myvals = myQueryParamArray[i].split(/\=/);
text += myvals[1] + ", ";
}
context.setVariable('mytext', text);
In this example I create a variable named mytext with the value of "X, Y,"

drupal_map_assoc($array)

function _ahah_example_get_first_dropdown_options() {
$stid = oci_parse($conn, "SELECt code,descr1 FROM dbtest.regions");
oci_execute($stid);
$region= array();
while (($row = oci_fetch_array($stid, OCI_ASSOC))) {
$region[$row['CODE']]= $row['DESCR1'];
}
$region['']='Select';
oci_free_statement($stid);
oci_close($conn);
return drupal_map_assoc($region);
}
but it returns the key and the value equal I need the original key to be returne cause im using it's value in a javascript function?anyone would know how to return the original Key?
From your code, you should be able to skip drupal_map_assoc and just return $region. Give that a try and see if you like the results.
Reference http://api.drupal.org/api/drupal/includes--common.inc/function/drupal_map_assoc/6

Flex Advance datagrid date column sorting problem

I'm trying to sort ArrayCollection with following dates, and getting unexpected results.
14-Apr-1980
01-Feb-1975
30-Dec-1977
27-Oct-1968
I'm using following code to sort,
private function sortDate(obj1:Object, obj2:Object):int
{
var d1:Number = (new Date(Date.parse(obj1.date))).getTime();
var d2:Number = (new Date(Date.parse(obj2.date))).getTime();
if(d1 < d2)
{
return -1;
}
else if(d1 == d2)
{
return 0;
}
return 1;
}
And it's called like ,
<mx:AdvancedDataGridColumn dataField="dob"
headerText="Date of birth:"
sortCompareFunction="sortDate"
dataTipFunction="dateFormat" />
</mx:columns>
Results are coming like ,
27-Oct-1968
01-Feb-1975
14-Apr-1980
30-Dec-1977
What am I missing ?
(Note : my date format in AdvancedDataGrid is DD-MMM-YYYY )
Change the date separator from hyphen (-) to a slash (/) or a space. And make sure that the sortableColumns property of AdvancedDataGrid is true
From the livedocs page for Date.parse() method.
The year month and day terms can be separated by a forward slash (/) or by spaces, but never by a dash (-).

Resources