Find nested value in json

I think i need to use a transformer, but have not found the right sequence. basically in nested Json, i need to find value of Agent_ID, when purpose = "agent". here is basic json dealing with. Purpose could show up multiple times in the json.

{
"entities": [
{
"id": "13027876-4f39-4541-a268-c71b568f99ad",
"startTime": "2024-02-21T21:42:26.123Z",
"address": "dfadf",
"participants": [
{
"id": "4c115c66-64cb-4eef-9202-7ef5bdeddffa",
"startTime": "2024-02-21T21:42:26.123Z",
"connectedTime": "2024-02-21T21:42:26.123Z",
"name": "Csdfasf",
"externalContactId": "dd05a1e1-5f6f-42d7-9f8a-4f66380f7093",
"queueId": "925b16a4-f8e2-4e1b-a175-48612c91e65b",
"purpose": "customer",
"address": "csdafadsf",
"wrapupRequired": false,
"mediaRoles": [
"full"
],
"attributes": {},
"emails": [
{
"state": "connected",
"initialState": "connected",
"id": "242c7b39-0614-4ff6-97a7-745392c3bdfb",
"held": false,
"subject": "test 4",
"messagesSent": 1,
"segments": [],
"direction": "inbound",
"connectedTime": "2024-02-21T21:42:26.167Z",
"autoGenerated": false,
"provider": "dsf Email",
"peerId": "d32c2b27-ab55-44ae-8599-da1391b168f9",
"messageId": "34c17e02-1588-43d3-ab16-192903b6dcce",
"draftAttachments": [],
"spam": false,
"afterCallWork": {},
"afterCallWorkRequired": false
}
]
},
{
"id": "4e8ffd78-8b16-46d8-a00e-bf7497f5f49b",
"startTime": "2024-02-21T21:42:26.140Z",
"endTime": "2024-02-21T21:42:27.068Z",
"connectedTime": "2024-02-21T21:42:26.140Z",
"purpose": "agent",
"address": "sdafasfa",
"wrapupRequired": false,
"mediaRoles": [],
"attributes": {},
"emails": [
{
"state": "disconnected",
"Agent_ID": "agent1",
"initialState": "connected",
"id": "d32c2b27-ab55-44ae-8599-da1391b168f9",
"held": false,
"subject": "test 4",
"messagesSent": 1,
"segments": [],
"direction": "inbound",
"disconnectType": "transfer",
"connectedTime": "2024-02-21T21:42:26.200Z",
"disconnectedTime": "2024-02-21T21:42:27.068Z",
"autoGenerated": true,
"provider": "sdaf Email",
"peerId": "242c7b39-0614-4ff6-97a7-745392c3bdfb",
"messageId": "0101018dcd9eb414-587fbaa3-3031-4885-9972-c438bbcec4dd-000000",
"draftAttachments": [],
"spam": false,
"afterCallWork": {},
"afterCallWorkRequired": false
}
]
},
}
}
}
}

yikes, I'm probably going to wake up at some point and realize I made a mistake but I'll give it a go.... sorry for the messy look, I'm sure there's a ton of JS sneaky stuff to make this look less confusing.

oh and ya this won't work as a Transformer (or idk, it might actually work as one w a change or 2 now that I think about it), but it is a search algo so just pass the function your object and it'll find the first object that meets your requirements.

this is based off of the depth-first search (DFS) used for trees normally.

// I'm assuming we're blinding walking through this object.  efficiency could be greatly increased if you know for sure the structure of the obj
let findAgentID = false;  // we declare this outside the function so we can call it recursively without overwriting the value.... we just have to remember to reset it.  we could also pass it in as a parameter, but for each recursive call we'd be increasing the stack size by more than is needed.  we could do the same for currIdFound but that one's a bit more important to remember to reset.
function serchJsonObj(currObj, currIdFound = "") {
  // loop through each object key
  currObj.keys().forEach((keyName) => {    
    switch (typeof currObj[keyName]) {
       // if currObj is a JSON obj
      case 'object':
        // we don't care about the whole object, we want to drill down to each property so call the same function again, but with the child/inside value so we can search it also.  we keep doing for every type until we hit the base type
        return searchJsonObj(currObj[keyName], currIdFound);
        break;
      case 'array':
        // we need to check each array item to make sure it isn't another array or an object
        currObj[keyName].forEach((arrayItem) => {
          return searchJsonObj(arrayItem, currIdFound);
        }
        break;
      // the base types are below here, they're the only ones who have a value we care about.
      case 'string':
        // search for the flag to signal we can return if we've found anything yet
        if(keyName === 'purpose' && currObj[keyName] === 'agent'){
          // set the flag to start searching for the Agent_ID
          findAgentID = true;          
        }

        // check for the winning lottery ticket
        if(keyName === 'Agent_ID'){
          // if all conditions have been met, we can return what we've found
          if(findAgentID){
            // we're about to return so we need to reset findAgentID
            findAgentID = false;
            // we found gold, lets bounce!  =)
            return currObj[keyName];
          }
          // we've found an Agent_ID, but we haven't found the signal to return it (purpose = 'agent')
          else{
            currIdFound = currObj[keyName];  // as long as we remember to recursively call this function with this variable, we can track an Agen
          }
        }
        break;
      default:
        break;
    }

  } // the property of currObj named keyName is not an array or object and we've checked for 'purpose' as well as 'Agent_ID'.  continue the loop, check the next property.

  // at this point:
  // if currIdFound !== "" and !findAgentID 
  // then we found an Agent_ID in an object that did not have a 'purpose' of 'agent'... but we did find one, so if you want to inform the caller you can uncomment the next line
  // return currIdFound;
}

let AgentID = searchJsonObj(currObj)

I really feel like I made that completely backwards for some reason. in a nutshell, it takes a JSON object and it checks every key/value (property). if it finds an array, it searches every item in the array for another object to search. if it finds an object, it pauses the current search and it starts searching the new object. if it finds a string, it checks if the key is 'purpose' w a value of 'agent' and signals that if an Agent_ID has already been found or if one will be found in the future to return that value immediately and stop everything.

big nutshell =/ sorry. I'm sure there's a better search algo/pattern for json objects of unknown or possibly wrong types/structure but I took that class YEARS ago and can't think of anything right now (no it's not a result of the YEARS that have gone by :innocent:... I'm not old!!! :sweat: :hugs: it's just, I swear the memories never existed :face_with_monocle:)

EDIT: i knew it. I was in bed and I thought of a fringe case where you might have a problem (if it doesn't apply to you ignore me, i'm awesomesauce :rofl: or i'm losin it). if an object has 'purpose' and 'agent' but it doesn't have an Agent_ID, it will continue to search for one in the rest of properties. not a big deal you'd think, BUT if the parent object itself doesn't contain it and a child (either array item or nested objects) does it will still return the Agent_ID it found. the oposite would be something like: if an object contains an Agent_ID with a 'purpose' of 'not agent' but the parent does have a 'purpose' of 'agent', it will return the parents Agent_ID as the parents and as the childs. hope that makes sense.... maybe I'll think of an easier way to describe that (preferably in my dreams but we'll see lol)

is that what you were looking for or did you need something a little different? I can help ya change it for your needs, just let me know

thanks for this! looks good...i am tweaking it and putting into our staging today. thanks again for this effort

1 Like