Delete a key in a dynamic structure name

Hi,

I want to create a function with two arguments :

  1. MainStructure is a structure with one or many levels (ex : myStructure.SubKey.SubSubKey…)
  2. KeyPath is the path for the key to be deleted (ex : SubKey.SubSubKey…)

I wrote some code to make it happen, but it’s still at the prototype stage

The first solution, deleteKeyWithReference, use the pointer of the structure to delete the key I want
The second solution, deleteKeyWithEvaluate, use the Evaluate function, but I am very uncomfortable using Evaluate for security reasons (or excessive paranoia)

<cffunction name="deleteKeyWithReference" returntype="void" output="no">
        
    <!--- Arguments --->
    <cfargument name="mainStructure" type="struct" required="yes">
    <cfargument name="keyPath" type="string" required="yes">
    
    <!--- Init the result --->
    <cfset var structurePointer = ARGUMENTS.mainStructure>
    <cfset var keyCount = 0>
    <cfset var keyIndex = "">

    <cfloop list="#ARGUMENTS.keyPath#" delimiters="." index="keyIndex">
        
        <cfif structKeyExists(structurePointer,keyIndex)>

            <cfset keyCount += 1>

            <cfif keyCount EQ listLen(ARGUMENTS.keyPath,".")>
                <cfbreak>
            </cfif>

            <cfset structurePointer = structurePointer[keyIndex]>

        </cfif>

    </cfloop>

    <cfset structDelete(structurePointer,listLast(ARGUMENTS.keyPath,"."))>

</cffunction>

<cffunction name="deleteKeyWithEvaluate" returntype="void" output="no">
        
    <!--- Arguments --->
    <cfargument name="mainStructure" type="struct" required="yes">
    <cfargument name="keyPath" type="string" required="yes">

    <cfset var levels = listDeleteAt(ARGUMENTS.keyPath,listLen(ARGUMENTS.keyPath,'.'),'.')>
    <cfset var lastKey = listLast(ARGUMENTS.keyPath,'.')>

    <cfif listLen(levels,'.') GT 0>
        <cfset structDelete(evaluate("ARGUMENTS.mainStructure.#levels#"),lastKey)>
    <cfelse>
        <cfset structDelete(ARGUMENTS.mainStructure,lastKey)>
    </cfif>

</cffunction>

<cfset myMainStructure = structNew()>
<cfset myMainStructure.A.SubKey.SubSubKey = "Hello world 1">
<cfset myMainStructure.B.SubKey.SubSubKey = "Hello world 2">
<cfset myMainStructure.C.SubKey.SubSubKey = "Hello world 3">
<cfset myMainStructure.D.SubKey.SubSubKey = "Hello world 4">


<h1>Before</h1>
<cfdump var="#myMainStructure#">

<cfset deleteKeyWithReference(myMainStructure,"A.SubKey")>
<cfset deleteKeyWithEvaluate(myMainStructure,"B.SubKey.SubSubKey")>
<cfset deleteKeyWithEvaluate(myMainStructure,"C")>

<h1>After</h1>
<cfdump var="#myMainStructure#">

Here the mainStructure
mainStructure

It’s working, but I wonder if there is a better way to do it? I tried several things to concatenate the variable (the structure) passed to structDelete, but no luck. I have the impression that the code could be simpler, but that a detail escapes me. Maybe it’s just a syntax problem.

Thank you

Am I missing something but structDelete is all you need isn’t it?

<cfscript>
    myMainStructure = {};
    myMainStructure.A.SubKey.SubSubKey = "Hello world 1";
    myMainStructure.B.SubKey.SubSubKey = "Hello world 2";
    myMainStructure.C.SubKey.SubSubKey = "Hello world 3";
    myMainStructure.D.SubKey.SubSubKey = "Hello world 4";
    dump(myMainStructure);
    structDelete(myMainStructure.A, 'SubKey');
    structDelete(myMainStructure.B.SubKey, 'SubSubKey');
    dump(myMainStructure);
</cfscript>

Hi @andrew,

structDelete(myMainStructure.B.SubKey, 'SubSubKey');

The first argument of structDelete must be dynamic. I pass to my function the first argument myMainStructure, then the second argument is the path of the key (as a string) I want to delete (ex. : “B.SubKey.SubSubKey” or “B.SubKey” or “B”).

It’s a component I created to manage session variables (in a specific way for my app) and to make it easier to read/write/delete session variables and all the logic to test if the variable exists before and/or return a default value if the variable doesn’t exists. So when I call this component, I always pass myMainStructure (it’s always exists in my app), but the path to the SubSubKey may not exists in some situation.

Maybe I need to step back and review all my component if I can do it differently.

Ok, I see now, you want to delete using a path instead of a key, like:

structDelete(myMainStructure, 'B.SubKey.SubSubKey');

Which seems reasonable given some of the functions like findKey return a path as part of their response. Sounds like a reasonable enhancement request to me, raise a ticket on JIRA for it. Doesn’t help you right now, but you have your functions that works for the time being.

@TonyMonast I had a flash of inspiration on how you could do this:

    var key_path = 'B.SubKey.SubSubKey';
    structDelete( structGet( 'myMainStructure.#key_path.listDeleteAt( key_path.listLen('.') , '.' )#' ) , key_path.listLast('.') );

Treat the key path as a list and use structGet which can get the structure by path.

1 Like

Hi @andrew,

It’s a great way to end the week on a high note! A big thank-you! It works very well on Lucee and ACF.

I will do something like that :

<cffunction name="deleteKeyWithPath" returntype="void" output="no">
        
    <!--- Arguments --->
    <cfargument name="mainStructure" type="struct" required="yes">
    <cfargument name="keyPath" type="string" required="yes">

    <cfset var levels = listDeleteAt(ARGUMENTS.keyPath,listLen(ARGUMENTS.keyPath,'.'),'.')>
    <cfset var lastKey = listLast(ARGUMENTS.keyPath,'.')>

    <cfif listLen(levels,'.') GT 0>
        <cfset structDelete( structGet( 'ARGUMENTS.mainStructure.#levels#' ) , lastKey )>
    <cfelse>
        <cfset structDelete(ARGUMENTS.mainStructure,lastKey)>
    </cfif>

</cffunction>


<cfscript>
    myMainStructure = {};
    myMainStructure.A.SubKey.SubSubKey = "Hello world 1";
    myMainStructure.B.SubKey.SubSubKey = "Hello world 2";
    myMainStructure.C.SubKey.SubSubKey = "Hello world 3";
    myMainStructure.D.SubKey.SubSubKey = "Hello world 4";
    dump(myMainStructure);
    var key_path = 'B.SubKey.SubSubKey';
    deleteKeyWithPath(myMainStructure,key_path);
    dump(myMainStructure);
</cfscript>

Thank you again!

I can’t mark your answer as the solution. Can a moderator change the category of my topic for dev and support?

@TonyMonast I’ve changed the topic as requested.

Thank you! Have a nice day!