Construct a string by combining the elements of a multi-dimensional array of children, organized into grouped

My task involves manipulating a complex, deeply nested array of nodes to create a specific query string structure. The desired format for the query string is as follows:

(FULL_NAME="x" AND NOT(AGE="30" OR AGE="40" AND (ADDRESS="y" AND STREET="z" AND NOT(USER="admin" OR USER="super admin"))) AND TITLE="Developer")

Here's an overview of the JSON data I'm working with:

  • A list of nodes that will receive user input.
  • An operator that handles multiple node connections.
  • If a user selects "NOT," the main container containing it will exclude certain nodes, resulting in a modified structure like
    NOT (X="y" AND Y="x")
    .
  • The primary vertical operators are used to join children nodes, and if a "NOT" operator is chosen, a parent container transforms into a group with another container inside containing "AND" or "OR" operators.
  • Users can select between "NOT," "AND," or "OR" operators but cannot use the same operator when grouping unless it's a "NOT" operator.

Represented in the form of an array:

[
   {
      "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
      "operator":"AND",
      "isMain":true,
      "nodes":[
         {
            "values":{
               "fieldValue":{
                  "FieldName":"ORIGINAL_FILE_NAME",
               },
               "operator":"=",
               "primaryOperandValue":"new"
            },
            "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
         }
      ],
      "children":[
         {
            "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
            "operator":"NOT",
            "nodes":[],
            "children":[
               {
                  "operator":"AND",
                  "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                  "nodes":[
                     {
                        "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                        "values":{
                           "fieldValue":{
                              "FieldName":"CONTROL_NUMBER",
                           },
                           "operator":"BETWEEN",
                           "primaryOperandValue":"x",
                           "secondaryOperandValue":"y"
                        }
                     }
                  ],
                  "children":[
                     {
                        "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                        "operator":"NOT",
                        "nodes":[],
                        "children":[
                           {
                              "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                              "operator":"OR",
                              "nodes":[
                                 {
                                    "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                    "values":{
                                       "fieldValue":{
                                          "FieldName":"CONTROL_NUMBER",
                                       },
                                       "operator":" > ",
                                       "primaryOperandValue":"30",
                                       "secondaryOperandValue":null
                                    }
                                 }
                              ],
                              "children":[]
                           }
                        ]
                     }
                  ]
               }
            ]
         },
         {
            "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
            "operator":"OR",
            "nodes":[
               {
                  "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"ENDS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  }
               },
               {
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"BEGINS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  },
                  "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
               }
            ],
            "children":[
               
            ]
         }
      ]
   }
]

This information is visually represented in the following UI screenshot: https://i.stack.imgur.com/LC3Bw.png

I've explored various methods to extract a string from this array structure, but I'm facing challenges when dealing with deeply nested node groups.

Thank you.

Answer №1

A strategy to consider is utilizing separate callbacks for nodes and children, then gathering the elements to form a string.

const
    data = [{ uuid: "b7f0ddf4-0290-4c7e-bb59-771aa46bc850", operator: "AND", isMain: true, nodes: [{ values: { fieldValue: { FieldName: "ORIGINAL_FILE_NAME" }, operator: "=", primaryOperandValue: "new" }, uuid: "779fb920-eb7f-4441-9b5a-886c7a41e271" }], children: [{ uuid: "7467b8c9-212e-41b8-ac02-04296b95c88c", operator: "NOT", nodes: [], children: [{ operator: "AND", uuid: "eaad7c96-0e8f-466b-a255-1075a8e68647", nodes: [{ uuid: "f6057d1b-56d7-4ee6-ac5b-332fbd180fd4", values: { fieldValue: { FieldName: "CONTROL_NUMBER" }, operator: "BETWEEN", primaryOperandValue: "x", secondaryOperandValue: "y" } }], children: [{ uuid: "95fd2b08-cc49-498a-bd9f-c50dc55bc39f", operator: "NOT", nodes: [], children: [{ uuid: "7637ecc1-28b4-47d7-a602-cd172fb5e269", operator: "OR", nodes: [{ uuid: "0598a915-5818-4c6e-a3d5-6724f893871a", values: { fieldValue: { FieldName: "CONTROL_NUMBER" }, operator: " > ", primaryOperandValue: "30", secondaryOperandValue: null } }], children: [] }] }] }] }, { uuid: "78218b5b-b18b-4418-beed-b3418361785f", operator: "OR", nodes: [{ uuid: "ec956407-4fc6-46df-baa7-d2233711dc20", values: { fieldValue: { FieldName: "EMAIL_ANY_ADDRESS" }, operator: "ENDS_WITH", primaryOperandValue: "log", secondaryOperandValue: null } }, { values: { fieldValue: { FieldName: "EMAIL_ANY_ADDRESS" }, operator: "BEGINS_WITH", primaryOperandValue: "log", secondaryOperandValue: null }, uuid: "6724e913-6e98-47b6-b6af-972a20f0173d" }], children: [] }] }],
    QUOTE = '"',
    wrap = string => `(${string})`,
    quote = string => `${QUOTE}${string}${QUOTE}`,
    isUnary = s => ['NOT'].includes(s),
    getNodes = ({ values: { fieldValue: { FieldName }, operator, primaryOperandValue, secondaryOperandValue } }) => secondaryOperandValue === null || secondaryOperandValue === undefined
        ? `${FieldName} ${operator.trim()} ${quote(primaryOperandValue)}`
        : `${FieldName} ${operator.trim()} ${quote(primaryOperandValue)} AND ${quote(secondaryOperandValue)}`,
    getChildren = ({ operator, nodes = [], children = [] }) => {
        const values = [...nodes.map(getNodes), ...children.map(getChildren)]
        return isUnary(operator)
            ? `${operator} ${values.join('')}`
            : wrap(values.join(` ${operator} `));
    },
    result = data.map(getChildren).join('');

console.log(result);

Answer №2

Consider implementing recursion for this task, here is an example:

const queryObject = [
   {
      "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
      "operator":"AND",
      "isMain":true,
      "nodes":[
         {
            "values":{
               "fieldValue":{
                  "FieldName":"ORIGINAL_FILE_NAME",
               },
               "operator":"=",
               "primaryOperandValue":"new"
            },
            "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
         }
      ],
      "children":[
         {
            "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
            "operator":"NOT",
            "nodes":[],
            "children":[
               {
                  "operator":"AND",
                  "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                  "nodes":[
                     {
                        "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                        "values":{
                           "fieldValue":{
                              "FieldName":"CONTROL_NUMBER",
                           },
                           "operator":"BETWEEN",
                           "primaryOperandValue":"x",
                           "secondaryOperandValue":"y"
                        }
                     }
                  ],
                  "children":[
                     {
                        "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                        "operator":"NOT",
                        "nodes":[],
                        "children":[
                           {
                              "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                              "operator":"OR",
                              "nodes":[
                                 {
                                    "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                    "values":{
                                       "fieldValue":{
                                          "FieldName":"CONTROL_NUMBER",
                                       },
                                       "operator":" > ",
                                       "primaryOperandValue":"30",
                                       "secondaryOperandValue":null
                                    }
                                 }
                              ],
                              "children":[]
                           }
                        ]
                     }
                  ]
               }
            ]
         },
         {
            "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
            "operator":"OR",
            "nodes":[
               {
                  "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"ENDS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  }
               },
               {
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"BEGINS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  },
                  "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
               }
            ],
            "children":[
               
            ]
         }
      ]
   }
]

const operatorString = (items) => items.map(
  ({operator, nodes, children, isMain }) => {
    
    const nodeString = nodes.map(({ values }) => {
      const {fieldValue, operator, primaryOperandValue, secondaryOperandValue} = values
      return fieldValue.FieldName + ' ' + operator + (secondaryOperandValue ?  '("' + primaryOperandValue + '", "' + secondaryOperandValue + '")' : ' "' + primaryOperandValue + '"')
    }).join(" " + operator + " ")
    
    return "( " + nodeString + ' ' + operator + operatorString(children) + " )"

  }
).join("")
console.log(operatorString(queryObject))

Answer №3

This code snippet may not be fully complete, but it should provide a solid foundation for you to start from.

function processNodes( node ) {
  var nodeStr = " Node [" + node.values.operator + "]";
  
  switch (node.values.operator) {
  
    case "=": 
    case " > ":
    case "<":
    case "ENDS_WITH":
    case "BEGINS_WITH":
      nodeStr = node.values.fieldValue.FieldName;
      nodeStr += " " + node.values.operator;
      nodeStr += " " + node.values.primaryOperandValue;
      break;
  }
  return nodeStr;
}

function processCommands ( cmdArray, operator ) {
  var cmdStr = '';

  for (var i = 0; i < cmdArray.length; i++)
  {
  
    if (cmdArray[i].nodes.length > 0) {  
    cmdStr += " ( ";
      for (var j = 0; j < cmdArray[i].nodes.length; j++) {
        if (j > 0) {
          cmdStr += " " + cmdArray[i].operator + " ";
        }
        cmdStr += processNodes(cmdArray[i].nodes[j]);
        
      }
      cmdStr += " ) ";
    }
    if ( cmdStr || cmdArray[i].children.length > 0) {
        cmdStr += " " + cmdArray[i].operator + " ";
    }
      
    if (cmdArray[i].children.length > 0) {      
      cmdStr += " " + processCommands(cmdArray[i].children, cmdArray[i].operator);
    }
  }

  return cmdStr;

}


var cmdArray = [
    {
       "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
       "operator":"AND",
       "isMain":true,
       "nodes":[
          {
             "values":{
                "fieldValue":{
                   "FieldName":"ORIGINAL_FILE_NAME",
                },
                "operator":"=",
                "primaryOperandValue":"new"
             },
             "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
          }
       ],
       "children":[
          {
             "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
             "operator":"NOT",
             "nodes":[],
             "children":[
                {
                   "operator":"AND",
                   "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                   "nodes":[
                      {
                         "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                         "values":{
                            "fieldValue":{
                               "FieldName":"CONTROL_NUMBER",
                            },
                            "operator":"BETWEEN",
                            "primaryOperandValue":"x",
                            "secondaryOperandValue":"y"
                         }
                      }
                   ],
                   "children":[
                      {
                         "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                         "operator":"NOT",
                         "nodes":[],
                         "children":[
                            {
                               "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                               "operator":"OR",
                               "nodes":[
                                  {
                                     "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                     "values":{
                                        "fieldValue":{
                                           "FieldName":"CONTROL_NUMBER",
                                        },
                                        "operator":" > ",
                                        "primaryOperandValue":"30",
                                        "secondaryOperandValue":null
                                     }
                                  }
                               ],
                               "children":[]
                            }
                         ]
                      }
                   ]
                }
             ]
          },
          {
             "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
             "operator":"OR",
             "nodes":[
                {
                   "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                   "values":{
                      "fieldValue":{
                         "FieldName":"EMAIL_ANY_ADDRESS",
                      },
                      "operator":"ENDS_WITH",
                      "primaryOperandValue":"log",
                      "secondaryOperandValue":null
                   }
                },
                {
                   "values":{
                      "fieldValue":{
                         "FieldName":"EMAIL_ANY_ADDRESS",
                      },
                      "operator":"BEGINS_WITH",
                      "primaryOperandValue":"log",
                      "secondaryOperandValue":null
                   },
                   "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
                }
             ],
             "children":[
                
             ]
          }
       ]
    }
 ];

console.log(processCommands(cmdArray));
 

Answer №4

If you're looking to achieve something similar, consider implementing a series of helper functions alongside a core recursive function.

const initialize = () => {
    
    //  custom logic for JavaScript to SQL conversion
    const nodeToClause = (node) => {
        let result = [];
        switch (node.values.operator) {
            case 'BETWEEN':
               result = [node.values.fieldValue.FieldName,node.values.operator,node.values.primaryOperandValue,node.values.secondaryOperandValue];
                break;
            case '=':
            case 'BEGINS_WITH':
            case 'ENDS_WITH':
                result = [node.values.fieldValue.FieldName,node.values.operator,`"${node.values.primaryOperandValue}"`];
                break;
            default:
               result = [node.values.fieldValue.FieldName,node.values.operator,node.values.primaryOperandValue];
                break;
        }
        return result.join(' ');
    };
    //  serialize
    const toSQL = (statement) => {
        let result = '';
        const glue = ' ' + statement.operator + ' ';
        if (statement.nodes.length) {
            result = statement.nodes.map(nodeToClause).join(glue);    
        }
        return result;
    };
    // helps with formatting. not required, but adds value
    const tabs = (n) => {
        const tab = ['\n'];
        let index = 0;
        while (index < n) {
            tab.push('\t');
            index++;
        }
        return tab.join('');
    };
    //  the recursive function that handles all the processing
    const recurse = (queryFragment, joiner, level=0) => {
        let result = "";
        if (Array.isArray(queryFragment)) {
            result = queryFragment.map(frag => {
                return recurse(frag, frag.operator, level);
            }).join(joiner);
        } else {
            result = tabs(level) + toSQL(queryFragment);
            if ("children" in queryFragment && queryFragment.children.length) {
                result += ' ' + queryFragment.operator + ' (' +  recurse(queryFragment.children,queryFragment.operator,level+1) + tabs(level) + ') ';
            }
        }
        return result;
    }; 
    
    //  load data into DOM
    const x = recurse(query,';');
    output.innerText = x;

};

// display on screen
window.setTimeout(initialize, 997);
const output = document.getElementById('output');

// sample data
const query = [
   {
      "uuid":"b7f0ddf4-0290-4c7e-bb59-771aa46bc850",
      "operator":"AND",
      "isMain":true,
      "nodes":[
         {
            "values":{
               "fieldValue":{
                  "FieldName":"ORIGINAL_FILE_NAME",
               },
               "operator":"=",
               "primaryOperandValue":"new"
            },
            "uuid":"779fb920-eb7f-4441-9b5a-886c7a41e271"
         }
      ],
      "children":[
         {
            "uuid":"7467b8c9-212e-41b8-ac02-04296b95c88c",
            "operator":"NOT",
            "nodes":[],
            "children":[
               {
                  "operator":"AND",
                  "uuid":"eaad7c96-0e8f-466b-a255-1075a8e68647",
                  "nodes":[
                     {
                        "uuid":"f6057d1b-56d7-4ee6-ac5b-332fbd180fd4",
                        "values":{
                           "fieldValue":{
                              "FieldName":"CONTROL_NUMBER",
                           },
                           "operator":"BETWEEN",
                           "primaryOperandValue":"x",
                           "secondaryOperandValue":"y"
                        }
                     }
                  ],
                  "children":[
                     {
                        "uuid":"95fd2b08-cc49-498a-bd9f-c50dc55bc39f",
                        "operator":"NOT",
                        "nodes":[],
                        "children":[
                           {
                              "uuid":"7637ecc1-28b4-47d7-a602-cd172fb5e269",
                              "operator":"OR",
                              "nodes":[
                                 {
                                     "uuid":"0598a915-5818-4c6e-a3d5-6724f893871a",
                                    "values":{
                                       "fieldValue":{
                                          "FieldName":"CONTROL_NUMBER",
                                       },
                                       "operator":" > ",
                                       "primaryOperandValue":"30",
                                       "secondaryOperandValue":null
                                    }
                                 }
                              ],
                              "children":[]
                           }
                        ]
                     }
                  ]
               }
            ]
         },
         {
            "uuid":"78218b5b-b18b-4418-beed-b3418361785f",
            "operator":"OR",
            "nodes":[
               {
                  "uuid":"ec956407-4fc6-46df-baa7-d2233711dc20",
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"ENDS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  }
               },
               {
                  "values":{
                     "fieldValue":{
                        "FieldName":"EMAIL_ANY_ADDRESS",
                     },
                     "operator":"BEGINS_WITH",
                     "primaryOperandValue":"log",
                     "secondaryOperandValue":null
                  },
                  "uuid":"6724e913-6e98-47b6-b6af-972a20f0173d"
               }
            ],
            "children":[
               
            ]
         }
      ]
   }
];
pre {
    -moz-tab-size:    4;
    -o-tab-size:      4;
    tab-size:         4;
    font-family: "Courier";
    font-weight: bold;
    color: saddlebrown;
}
body {
   background-color: #bbbf3e94;
}
<pre class="text" id="output"></pre>

Answer №5

You should consider implementing a PRE-ORDER traversal algorithm for tree structures.

Check out more information on tree traversal here.

Update

To traverse all nodes in the tree:
Step 1 − Visit the root node.
Step 2 − Recursively traverse the left subtree.
Step 3 − Recursively traverse the right subtree.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Adding a class to the following DIV and smoothly transitioning the current one out can be achieved using a dynamic number of items in multiple instances

Is there a way to create a scalable CSS slideshow for text divs without using images? Here is the current HTML structure: <div class="mb-panel-container cf"> <div class="mb-panel-section mb-slider"> <div class="mb-panel active" ...

"Utilizing JSON parsing in Node.js and rendering the data in a Jade template

I need assistance with parsing JSON and presenting the response in a tabular format using Jade. Can you help me display the key-value pairs in two separate columns? Node.js exports.postMQinput = function(req, res) { req.assert('name', 'Q ...

Safari experiencing hang after successful CORS OPTIONS request

Safari (10 - OSX El Capitan) Issue with CORS Encountering a problem with CORS while performing a POST request from an AngularJS front-end to a Laravel back-end. In Chrome and Firefox, both OPTIONS and POST requests return http status 200 OK In Safari 10 ...

Retrieve information from a variety of selected checkboxes

Is there a way to retrieve the values of check boxes that are generated dynamically? @ $db = mysql_connect("abc", "abc", ""); mysql_select_db("abc"); $strSQL = "SELECT * FROM student"; ...

The Ubiquity CmdUtils.setSelection function does not support replacing HTML code

I'm working on a Ubiquity command to replace selected links or URLs pointing to images with the actual image itself. However, I've found that the CmdUtils.setSelection() function doesn't work when there are html tags in the selection, render ...

What is the best way to assign the selected attribute to my mat-option element?

I am encountering an issue with my mat-select and mat-option control. I am trying to apply the selected attribute to the first mat-option control without using binding on [(ngModel)] or [(value)]. The mat-option control is being generated by the *ngFor d ...

Choose from the options provided to display the table on the screen

One of the challenges I am facing involves a table with two columns and a select option dropdown box. Each row in the table is associated with a color - green indicates good, red indicates bad, and so on. My goal is to have all values displayed when the pa ...

What is the preferred approach: displaying a message using a service or a directive?

I'm feeling a bit unsure. I understand that anything interacting with the DOM should be created as a directive. However, I find it more convenient to simply call my notification service to display error or success messages. This service internally cr ...

Fuel Calculation - Unable to pinpoint the error

My JavaScript Code for Calculating CO2 Emissions I'm working on a program that calculates how much CO2 a person produces per kilometer. This is part of my school project and I'm still learning, so please bear with me... I also just signed up on ...

Code Wizard

I am currently working on a project to develop an HTML editor. How it Needs to Function Elements Inside: Text Area - Used for typing HTML as text Iframe - Displays the typed HTML as real [renders string HTML to UI] Various Buttons Functionality: When ...

Retrieving all key value pairs from an intricate JSON payload with PHP

After receiving a JSON file from the server, I am facing the challenge of extracting all key:value pairs using PHP. Despite my efforts, my success so far has been limited. Has anyone encountered this issue before? I have been unable to find a solution yet ...

Guide user to different screen in React Navigation depending on context state

In my project, I have introduced a new state called roleSelected. Initially, the value of this state is set to false, and it is managed within my AuthContext. const [roleSelected, setRoleSelected] = useState(false); The configuration of my stack navigatio ...

Displaying JSON data based on a specific key

My current challenge involves dealing with a JSON string structured like this: {"cat1":"m1","cat2":["d1","d2","d3"],"cat3":["m1","m2","m3","m4"]} As part of my learning process in Javascript and AJAX, I am attempting to display the different values based ...

What causes the issue of Angular 9 routing breaking upon page refresh?

I have deployed my Angular 9 application on Heroku and everything is working perfectly. However, when I refresh the page or copy/paste a link, I get an error message saying "Cannot GET /XXX/XXX". Only the root link seems to work! Initially, I can navigate ...

Activate the ajax function using a specific reference

I have been working on creating an ajax function that contains all the data inside the variable called $item within the following function: public function handleAjaxData(&$item,&$params,$limitstart) { $view = JRequest::getVar('view' ...

Converting interfaces into mapped types in TypeScript: A guidance

Understanding Mapped Types in TypeScript Exploring the concept of mapped types in TypeScript can be quite enlightening. The TypeScript documentation provides a neat example to get you started: type Proxy<T> = { get(): T; set(value: T): void ...

Sending JSON data results

I received this JSON response: {"data":[{"series":{"id":"15404","series_code":"TOS","publisher_id":"280","series_short_name":"Tales of Suspense","start_year":"1959","end_year":"1968","published":"1959-1968","type_id":"1","no_issues":"99","published_ ...

What is the process for determining the total of elements within an array?

Imagine you have the following array: const items = [ { "amount1": "100", "amount2": "50", "name": "ruud" }, { "amount1": "40", "amount2": "60", "name": "ted" } ] Your goal is to calculate the sum of all amount1 and amount ...

Generating a personalized array by matching an array with an object using JavaScript / Node.js

let obj ={ 97:{ data:"OS", location:"system", access:"globally", parameters:"aed31" }, 490:{ data:"Ae-ds", location:"rootpath", access:"admin rights", parameters:"4fsje" }, 278:{ data:"config", location:"system", ...

Using Javascript to display or conceal a specific child element within a loop, based on which parent element has been clicked

I need assistance with creating a grid of projects where clicking on a specific 'project' in the loop will display the corresponding 'project_expanded' div, while hiding all other 'project_expanded' divs. My initial plan was ...