ColdFusion Query -- Get index of column by column name - coldfusion

I have a column name and, for the sake of SpreadsheetSetCellFormula, I want to get the index of that column from its name. This is important for expandability, as columns may be added or taken away in the future.
When I use queryName.ColumnList, ColdFusion automatically alphabetizes the list. However, passing it into SpreadsheetAddRows dumps the columns in original order. How can I get the index of a column from its name?

<!--- get the column list, in the original order, as a coldfusion compatible array --->
<cfset variables.columnArray = createObject("java","java.util.Vector").init(
createObject("java","java.util.Arrays").asList(
query.getColumnList()
)
)/>
<!--- get the index of the column. note that this is case sensitive. --->
<cfset variables.myColumnIndex = variables.columnArray.indexOf("MY_COLUMN")/>
No looping required.

This doesn't directly answer your question, however this may be VERY useful in your situation (I have used it in a similar situation)
This is taken from:
http://existdissolve.com/2010/11/quick-coldfusion-goodness/
columns = arrayToList(myquery.getMeta().getcolumnlabels())
gives you a list of the columns in their original order, with their original case sensitivity (not all upper case)

You can use the getMetaData() function, which you can read about here. This function will return an array of Columns in the correct order, including some other information. You would use it as follows:
getMetaData(queryName)

Related

Converting JSON into Table (PowerQuery)

What would be a correct PowerQuery syntax to extract the information from this Web JSON into a table:
I'm not very familiar with PowerQuery, and this is probably the only time I'll need this, so I'd be grateful if someone would help me out without refering me to documentation. Thanks
[{"time_entry_group": {"minutes": 301,"time_entries_params": {"locked": "0","from": "2021-02-01","to": "2021-02-28","customer_id": "11223344","project_id": "223388","service_id": "435248"},"revenue": 57691.6666666667,"project_id": 223388,"project_name": "Scrb","service_id": 435248,"service_name": "Meetings","month": "202102"}}
, {"time_entry_group": {"minutes": 1175,"time_entries_params": {"locked": "1","from": "2021-01-01","to": "2021-01-31","customer_id": "11223344","project_id": "223388","service_id": "421393"},"revenue": 225208.333333333,"project_id": 223388,"project_name": "Scrb","service_id": 421393,"service_name": "Design","month": "202101"}}
, {"time_entry_group": {"minutes": 24,"time_entries_params": {"locked": "1","from": "2021-01-01","to": "2021-01-31","customer_id": "11223344","project_id": "3168911","service_id": "95033"},"revenue": 4600.0,"project_id": 3168911,"project_name": "youkn Dev","service_id": 95033,"service_name": "Reviews","month": "202101"}}]
For future reference, if you have a column that you need to expand, you can instead click this arrow icon to the right of the column name. Clicking it should display a menu that should then allow you to specify which nested columns you want to get expand or get at. To be clear, it will expand that column for all rows in that table, not just one.
The JSON you've included is basically an array of objects, so maybe use:
Json.Document to parse the JSON, which should give you a list of records
Table.FromRecords to turn the list of records into a table.
Table.ExpandRecordColumn to expand a nested record columns.
Example implementation:
let
json = "[{""time_entry_group"":{""minutes"":301,""time_entries_params"":{""locked"":""0"",""from"":""2021-02-01"",""to"":""2021-02-28"",""customer_id"":""11223344"",""project_id"":""223388"",""service_id"":""435248""},""revenue"":57691.6666666667,""project_id"":223388,""project_name"":""Scrb"",""service_id"":435248,""service_name"":""Meetings"",""month"":""202102""}},{""time_entry_group"":{""minutes"":1175,""time_entries_params"":{""locked"":""1"",""from"":""2021-01-01"",""to"":""2021-01-31"",""customer_id"":""11223344"",""project_id"":""223388"",""service_id"":""421393""},""revenue"":225208.333333333,""project_id"":223388,""project_name"":""Scrb"",""service_id"":421393,""service_name"":""Design"",""month"":""202101""}},{""time_entry_group"":{""minutes"":24,""time_entries_params"":{""locked"":""1"",""from"":""2021-01-01"",""to"":""2021-01-31"",""customer_id"":""11223344"",""project_id"":""3168911"",""service_id"":""95033""},""revenue"":4600,""project_id"":3168911,""project_name"":""youkn Dev"",""service_id"":95033,""service_name"":""Reviews"",""month"":""202101""}}]",
parsed = Json.Document(json),
initialTable = Table.FromRecords(List.Transform(parsed, each [time_entry_group])),
expanded = Table.ExpandRecordColumn(initialTable, "time_entries_params", {"locked", "from", "to", "customer_id"})
in
expanded
One thing about the code above is that it doesn't expand nested fields project_id and service_id (present within time_entries_params). This is because these columns already exist in the table (and having duplicate column names would cause an error). I've assumed this isn't a problem, as the nested values aren't different.

UNIQUE formula in Google Sheets for multiple ranges

I have a list of participants in column A. A full employee list in column B. I want to get the list of non-participants in column C. Basically 'B-A' but in list form.
'January' is the participants list:
try:
=FILTER(A:A; NOT(COUNTIF(B:B; A:A)))
It is always an added challenge to write formulas when we don't have access to actual date. But based on what I can see, try this formula in the top cell of any empty column:
=ArrayFormula({"My Header"; FILTER(R2:R,ISERROR(VLOOKUP(TRIM(R2:R),TRIM(T2:T),1,FALSE)))})
You can change "My Header" to something meaningful.
The next part means "FILTER in anything in the range R2:R that cannot be found [i.e., ISERROR(VLOOKUP(...))] in T2:T."
TRIM is used just to account for any accidental/stray spaces that may occur in either list, since that would result in no match if one or the other had extra space.
If this does not do what you expect, please share a link to a sample spreadsheet.

Can you filter rows in a Query of Queries using a property value of a struct column value?

If an object, such as an Array or Struct is used as the column value of a row in a CF query object. Can properties of that object be used in the WHERE clause of a query of queries to limit the result set?
Given:
<cfset local.exampleArray=[
{ id:1,
nestedArray:["Tom","Dick","Harry"],
nestedStruct:{nid:42,name:"unknown"}
},
{ id:2,
nestedArray:["John","Paul","Ringo","George"],
nestedStruct:{nid:12,name:"rockstars"}
},
{ id:3,
nestedArray:["Bonny","Clyde"],
nestedStruct:{nid:43,name:"criminals"}
},
]>
<cfset local.exampleQuery=queryNew("id,nestedArray,nestedStruct","integer,object,object",local.exampleArray)>
The queries of queries:
<cfquery dbtype="query" name="local.exampleQoQ">
SELECT *
FROM [local].exampleQuery
WHERE nestedStruct.nid=12
</cfquery>
<cfquery dbtype="query" name="local.exampleQoQ2">
SELECT *
FROM [local].exampleQuery
WHERE nestedArray.length=3
</cfquery>
Results in the query of queries runtime error:
nestedStruct.nid/nestedArray.length does not match any table in FROM table list
When not using the object type columns in the WHERE clause, the objects are returned correctly when queried and behave as expected:
<cfquery dbtype="query" name="local.exampleQoQ">
SELECT *
FROM [local].exampleQuery
WHERE id=1
</cfquery>
<cfoutput query="local.exampleQoQ">
#local.exampleQoQ.id#:#ArrayLen(local.exampleQoQ.nestedArray)#:#local.exampleQoQ.nestedStruct.nid#
</cfoutput>
Will result in "1:3:42"
Is this just an issue where the QoQ implementation doesn't support accessing the properties of a column value object?
As I mentioned earlier, a database query can have a column with array/structure-ish data, but that's not really what a database is for. As you've seen, it makes querying for the data you want more difficult than it should be, and is really treating a database as little more than a place to store data.
Anyway, you seem to want to filter your query records by a specific value that's contained inside one column's structure data and also filter those results if another columns array data contains a certain number of records.
You don't want Query of Query for this. It's already a highly limited "query" aspect of CF, and should be used only when necessary. If you are using ColdFusion 2016+, you can use a function that was added: queryFilter().
Using your above setup under "Given:", you can use the following:
<cfscript>
/* Instead of QoQ, limit your Query with queryFilter() */
filteredQuery = queryFilter( exampleQuery
,function(o){ return o.nestedStruct.NID == 12 ;
}
) ;
</cfscript>
Which will give you a variable filteredQuery that contains:
Then you can just address filteredQuery.nestedArray to get your array of "John, Paul, George and Ringo".
But you also want to filter for the array in nestedArray to be 3 elements. So you can just add another condition to your callback return:
local.filteredQueryForLength = queryFilter(
local.exampleQuery2,
function(o){ return o.nestedStruct.NID == 12 && arrayLen(o.nestedArray) == 3 ; }
) ;
Which then gives you an empty Query Object, since there are 4 elements to the filteredQuery.nestedArray that you selected.
Finally, queryFilter has a member function that is simply filter(), so you can be even shorter and use this:
local.filteredQueryForLength2 = local.exampleQuery3.filter(
function(o){ return o.nestedStruct.NID == 12 && o.nestedArray.len() == 3 ; }
) ;
Also remember that ColdFusion Query Objects are Pass-By-Reference, so if you do anything (like filter()) that modifies the object, it will change that base object so it will be different if you use it again. Which also means that you don't have to assign it to a variable. You can just call queryFilter and then reference your original query object.
And another note: when using CF Script syntax (which I much prefer), don't forget that=is assignment and==is comparison. I forgot that initially and all of the records were returning withnestedStruct.NIDas12`. :-/
Last note: I created a Fiddle at https://trycf.com/gist/031a090059a46cd471aa44627fc7ee12/acf2016?theme=monokai. I added one extra element to your mocked query, so that you could see what your return object looks like with multiple elements matching the filters.

Coldfusion query of queries with row numbers passed to second query

I'm doing the following query of queries. The first dump of the query shows the entire excel sheet, the second dump shows the results of the second query.
I'm doing a validation check of the excel sheet to make sure there are no more pit bulls breeds brought into the system and I need to be able to tell the user which row on the excel sheet has the pit-bull.
How do I get the row numbers from the first row to appear in the second row? So the user can make changes on the excel sheet.
Do I have to resort to editing the excel document when it was first uploaded into the server and add a row number column to it? There is probably a better way to accomplish this.
<cffunction name="validateExcelSheet" access="public" output="yes" returnType="void"
hint="check dogs">
<cfspreadsheet
action="read"
src="#SESSION.theFile#"
headerrow= "1"
excludeHeaderRow = "true"
query = "allData"
rows = "1-#lastRow#" />
<cfscript>
pitBullcheck = new Query(
sql ="SELECT * FROM allData where breed like 'Pit%' ",
dbtype = "query",
allData = allData);
pitBullresult = pitBullcheck.execute().getResult();
</cfscript>
</cffunction>
Here's a tag based version of cfquery
<cfquery name="pitBullresult" dbtype="query">
SELECT *
FROM allData
WHERE breed LIKE 'Pit'
</cfquery>
That is not something you can do with cfspreadsheet. CFSpreadsheet only returns the cell values. It does not provide the physical row numbers, within the spreadsheet, that contained those values.
Also, something else to keep in mind is that CFSpreadsheet only returns "logical" (ie populated) rows/cells. That is not the same as the "physical" row numbers 1,2,3,... and column headers A,B.C.... that you see in Excel. Since users can enter values anywhere within a spreadsheet, logical and physical are not always the same thing.
For example, create a blank spreadsheet. Then enter values in cells
A2 and A25. Now run your code above. While you might expect the
resulting query to contain twenty-five (25) records, it will only
contain two (2), because only two cells were populated.
query
Row | COL_1
1 | Value in cell A2 (physical row 2)
2 | Value in cell 25 (physical row 25)
I think best you could do with cfspreadsheet is to loop through the query and display the relative row number within the results ie query.currentRow. If the populated data always starts within the first few rows, that might be good enough for your purposes. If not, it could get a bit confusing ...
That said, technically you could get the real physical row numbers. However, it requires much lower level code, which quite honestly ... seems like a lot of work, for very little gain.

Searching for unmatched ntheames when comparing spreadsheets

In one spreadsheet I have 3 columns with a first and last name of a person combined. In the 2nd spreadsheet, I have column a = first name and column b = last name.
I want to know which names in spreadsheet one cannot be found in spreadsheet two. I also need to verify the data to make sure that the formula was accurate on finding the correct lookup.
Do I have to combine my columns in spreadsheet 2 to make the first and last name in the same column to make this work?
Which formula would you use for either scenario?
Use this:
=ISNA(MATCH($A1&" "&$B1,Sheet2!$A:$A,FALSE)))
Where (in order):
A1 is the first name column in Sheet1
B1 is the last name column in Sheet1
Sheet2 is the sheet that has the data stored as names separately
$A:$A is the rows that have the two names together
FALSE is because it's an exact match
This will return FALSE if the element does not exist, and TRUE if it does
You can also use:
=VLOOKUP($A1&" "&$B1,Sheet2!$A:$D,3,FALSE)
If you want to retrieve data for a match.
Finally, if you need to do your lookups the other way, take a look at this thread for some ideas on how to split the string into two pieces.