How to delete a key/value pair from a struct in Coldfusion? - struct

(using Coldfusion8/MySQL5.0.88)
I'm storing JSON strings in a database. Strings consist of an id (key) and a number of items in a list (values).
Values look like this:
LOCAL.dropRecall = {"994323":"596895,596871,596864,596888,596840abc,596833,596826","991234":"9999,8888,abced"}
My problem is trying to delete a key/value pair. I'm trying like this:
<cfif StructKeyExists(LOCAL.dropRecall,"#Session.id#")>
<cfdump output="e:\dump.txt" label="catch" var="detected">
<cfset StructDelete( LOCAL.dropRecall,"#Session.id#", "true")>
</cfif>
which correctly detects the key/value pair (detected is reported), but deleting it does not work.
Question:
What am I doing wrong? Why is the key/value pair not removed?
EDIT:
Ok. Found it. I queried the database for field items, set this to LOCAL.dropRecall and wrote items back into the database... so the structDelete worked, but I did not write the empty struct back to the database.

Check that the value of session.id is what you think it is (no extra whitespace or anything like that). I tried a modification if your code on CF8, and it works fine for me:
<cfset variables.id = 991234>
<cfset LOCAL.dropRecall = deserializeJson('{"994323":"596895,596871,596864,596888,596840abc,596833,596826","991234":"9999,8888,abced"}')>
<cfset LOCAL.safeCopy = duplicate(LOCAL.dropRecall)>
<cfif StructKeyExists(LOCAL.dropRecall,"#variables.id#")>
<cfset StructDelete( LOCAL.dropRecall,"#variables.id#", "true")>
</cfif>
<cfdump var="#LOCAL#">
Does that code not work for you?

I wonder if the problem is that your variable names inside LOCAL.dropRecall start with a number? ColdFusion variables should always begin with a letter, underscore, or Unicode currency symbol.

Updated version of Adam's solution for ColdFusion 2016
<cfscript>
variables.id = 991234;
LOCAL.dropRecall = deserializeJson('{"994323":"596895,596871,596864,596888,596840abc,596833,596826","991234":"9999,8888,abced"}');
LOCAL.safeCopy = duplicate(LOCAL.dropRecall);
LOCAL.dropRecall.delete(variables.id); // you don't have to test if it is there
writedump(LOCAL);
</cfscript>

Related

ColdFusion - Taking a string and breaking it down into individual variables

I am receiving a string from another application that contains various pieces of information. The items are always in the same order, but the length of variable information can change. Each item is separated by an underscore and prefixed by a letter and colon.
Example:
A:12345678_B:5482945_C:20220911_D:20230402_E:3.94
Ideally, I want to break it down so that (in Coldfusion) I can end up with a series of variable that I would set as the values of A,B,C,D and E above.
Does anyone know any easy way to do this?
Thanks!
I think #Will is missing one small part of your requirement, which is
I can end up with a series of variable[s] that I would set as the values of A,B,C,D and E above"
To me this demonstrates more what you want to achieve:
raw = "A:12345678_B:5482945_C:20220911_D:20230402_E:3.94"
asArray = raw.listToArray("_")
asStruct = asArray.reduce((struct, kvAsString) => {
var key = kvAsString.listFirst(":")
var value = kvAsString.listRest(":")
struct[key] = value
return struct
}, {})
writeDump(asStruct)
(runnable # trycf.com: https://trycf.com/gist/e84aea475957e27b5dea2643e7c207ad/acf2021?theme=monokai)
Whilst this does not create "a series of variables" it does separate out the key from the value, and from there you can append that to whatever scope you need the variables in (eg: variables.append(asStruct))
In future please:
show us what you've already tried
give us the full intended result, don't just describe it.
Basically: always include code when you ask questions.
Gives you an array of the items. If you're handy with Regular Expressions, this could be even simpler. This is Lucee compatible. Adobe CFML didn't like the var commands outside of a function.
<cfscript>
var aNewItems = [];
var sString = "A:12345678_B:5482945_C:20220911_D:20230402_E:3.94";
var aItems = listToArray(sString, "_");
for( var sLetter in aItems){
aNewItems.append(listFirst(sLetter, ":"));
}
writeDump(aNewItems);
</cfscript>
Here's an Adobe CFML version that worked on TryCF.com:
<cfscript>
local.aNewItems = [];
local.sString = "A:12345678_B:5482945_C:20220911_D:20230402_E:3.94";
local.aItems = listToArray(local.sString, "_");
for( local.sLetter in aItems){
local.aNewItems.append(listFirst(local.sLetter, ":"));
}
writeDump(local.aNewItems);
</cfscript>
And just for fun, here is a list function answer:
<cfset TheString = 'A:12345678_B:5482945_C:20220911_D:20230402_E:3.94'>
<cfoutput>
<cfloop list = #TheString# index = "NameValue" delimiters = "_">
Variable #ListFirst(NameValue, ":")# = #ListLast(NameValue, ":")# <br>
</cfloop>
</cfoutput>
Run code here ==> https://trycf.com/gist/34321917c06643de1d3c25b611243466/acf2021?theme=monokai

Coldfusion - can you treat a string as list with no delimiters?

I previously thought that you can take an ordinary string and treat it as a list with "" as the delimiter, but CF doesn't seem to allow that. Doing something like this:
<cfloop list="abcdef" delimiters="" index="thisLetter">
<cfoutput>#thisLetter#</cfoutput><br>
</cfloop>
only results in 1 iteration of the loop, and an output of the whole string on one line. Not what's desired.
I did find you can use the Java string.split() method with a "" delimiter:
<cfset myArray = "abcdef".split("")>
<cfdump var="#myArray#">
But then the first element in the resulting array is an empty string, so there's an extra step in removing that from the array.
Is there a more elegant way to iterate over all the chars in this string without having to do any special string manipulation first, and without having to use left(), right(), or mid()?
I recommend using Java's String.toCharArray():
<cfset myString = "aäй漢か">
<cfset theChars = myString.toCharArray()>
<cfloop array="#theChars#" index="char">
<cfoutput>#char#</cfoutput>
</cfloop>
Looks like this is what I was looking for: https://www.bennadel.com/blog/307-ask-ben-iterating-over-the-characters-in-a-string.htm
Using mid() is simplest, and isn't too cumbersome. Still, would be nice to be able to use CF list loop with delimiters="".

cfspreadsheet write to xlsx truncates some data

I can't figure out what's going on. I'm exporting data from our Oracle 12c db to Excel from a simple select query, using cfspreadsheet:
<cfset var_filenameis = "report.xlsx">
<cfset SpreadsheetObj = spreadsheetNew("true")>
<cfset SpreadsheetObj = spreadsheetNew("#var_filenameis#","yes")>
<cfspreadsheet action="write" filename="#var_filenameis#" query="get_data" overwrite="true">
<cflocation url = "#var_filenameis#">
One column of data contains catalog numbers in various formats. Some of them become truncated when exported to xlsx. For example, 02923F becomes 2923 and 08552D becomes 8552. However, 08566A stays 08566A and 02584C also stays the same. The data shows correctly when viewed in the browser. Direct export from the DB also shows correct data. I could understand the leading 0 disappearing, but not the letter.
I have re-entered the problem catalog numbers in the DB to make sure there were no extra characters, to no avail. If I add a single quote in front of the catalog number, the display is correct except that I can't have a single quote showing in the output.
I get the same result with CF9 and CF11. I can't even tell if the problem is with cfspreadsheet or xlxs. Any good ideas? Thanks!
(Too long for comments)
If you are using CF11, try using SpreadSheetAddRows instead of cfspreadsheet. It is a bit smarter and uses the data types of the query columns to determine the proper cell type. As long as the query column is some sort of VARCHAR, the string value will be preserved.
<!--- sample data --->
<cfset qData = queryNew("")>
<cfset queryAddColumn(qData, "StringValue", "varchar", ["02923F","08552D","08566A","02584C"])>
<!--- populate sheet --->
<cfset sheet = SpreadsheetNew("Sheet1", true)>
<cfset spreadSheetAddRows(sheet, qData)>
<!--- display results --->
<cfheader name="Content-Disposition" value="attachment;filename=testFile.xlsx">
<cfcontent type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" variable="#SpreadSheetReadBinary(sheet)#">
I can't figure out what's going on
It is because POI (or possibly CF) interprets the "F" and "D" as special literals indicating the values are floating point numbers:
Floating-Point Literals
A floating-point literal is of type float if it ends with the letter F
or f; otherwise its type is double and it can optionally end with the
letter D or d.
So your strings are being converted to numbers. That is why the leading zero and trailing "F" or "D" disappear.

How can I remove a string/number from a list of numbers in Coldfusion?

Only stupid problems today... (still running coldfusion8)
I'm still messing with my list of strings. Now I need to remove 1 number out of the list...:
<cfdump output="e:\dump.txt" label="catch" var="--- drop ---">
<cfdump output="e:\dump.txt" label="catch" var="#ToString(variables.searchString)#">
<cfdump output="e:\dump.txt" label="catch" var="#ToString(variables.updateArticle)#">
<cfset Replace(ToString(variables.searchString), ToString(variables.updateArticle), "")>
<cfdump output="e:\dump.txt" label="catch" var="--- drop ----">
<cfdump output="e:\dump.txt" label="catch" var="#variables.searchString#">
My dump shows the following:
--- drop ----
596925,596864,596871
596925
---- done ----
596925,596864,596871
Question:
Any idea, why this is not working? ToString already is a desperation attempt...
Thanks
var foundAt = listFind(searchString, updateArticle);
if (foundAt)
searchString = ListDeleteAt(searchString, foundAt);
I haven't used coldfusion since cf8 came out but aren't you supposed to do:
<cfset variables.searchString = Replace(variables.searchString, variables.updateArticle, "")>
The replace function returns a value. You're currently using it as though you were doing a direct output.
<cfset fixedText = Replace(ToString(variables.searchString), ToString(variables.updateArticle), "")>

coldfusion string comparison

I have a form that returns a list like this when submitted:
2009,9
I want to compare it to database pulled values but keep getting an error.
<cfif #FORM.month# eq #qGetDates.year#,#qGetDates.month#>
I know I probably have to cast it or convert it to a string for the comparison to work, how do I do this?
Thanks,
R.
<cfif FORM.month eq "#qGetDates.year#,#qGetDates.month#">
or
<cfif compare(FORM.month, "#qGetDates.year#,#qGetDates.month#") EQ 0>
You are overusing #. Unless variables are inside quotation marks or a cfoutput block, you don't use # as a general rule.
Another rule: You must use quotes around strings (the comma in this case). You can also include variables in your strings with the rule above (use #) as seen in Henry's example.
<cfif #FORM.month# eq #qGetDates.year#,#qGetDates.month#>
should have # removed and the comma needs the string concatenated
<cfif FORM.month eq qGetDates.year & "," & qGetDates.month>
Or as Henry said
If you want to get the second value (a value after first comma), then
<cfset x = "2009,7">
<cfoutput>
#listGetAt(x,2)#
</cfoutput>

Resources