Discover the route of a string within an object or array

Given a specific object or array structure, I am looking to verify the existence of a certain path within it.

Example 1:

const path = "data/message";
const info = {
  data: {
    school: 'yaba',
    age: 'tolu',
    message: 'true'
  },
  time: 'UTC',
  class: 'Finals'
}

If body.data.message exists in the path, the function should return true; otherwise, it should return false.

Example 2:

const path = "data/message/details/lastGreeting";
const info = {
  data: {
    school: 'yaba',
    age: 'tolu',
    message: {
       content: 'now',
       details: {
          lastGreeting: true
       }
    }
  },
  time: 'UTC',
  class: 'Finals'
}

If body.data.message.details.lastGreeting can be located in the given path, the function should return true; otherwise, it should return false.

In cases where the body contains an array:

Example 3:

const path = "data/area/NY";
const info = {
  data: {
    school: 'yaba',
    age: 'tolu',
    names : ['darious'],
    area: [{
       NY: true,
       BG: true
    }],
    message: {
       content: 'now',
       details: {
          lastGreeting: true
       }
    }
  },
  time: 'UTC',
  class: 'Finals'
}

If body.data.area[0].NY is present as indicated by the path, the function should return true; otherwise, it should return false.

Here is the solution that has been devised:

const findPathInObject = (data, path, n) => {
  console.log('entered')
  console.log(data, path)
  

  if(!data){
    return false
  }
  
  let spath = path.split('/');
  for(let i = 0; i<n; i++){
    
    let lastIndex = spath.length - 1;
    if(spath[i] in data && spath[i] === spath[lastIndex]){
      return true
    }
    
    const currentIndex = spath[i];
//     spath.splice(currentIndex, 1);
    return findPathInObject(data[spath[currentIndex]], spath[i+1], spath.length)
    
  }
  
  return false
}

console.log(findPathInObject(info, path, 3))

Answer №1

To optimize the process, you can run an initial check to see if the path is empty and immediately return true.

Using an array, you can efficiently exit the function by comparing the array elements with the current path without considering indexes.

When checking for a specific key, verify its existence first and then either recursively call the function with the remaining path or return false if the key is not found in the object.

const
    findPathInObject = (data, path) => {
        if (!path) return true;
        if (!data || typeof data !== 'object') return false;
        if (Array.isArray(data)) return data.some(d => findPathInObject(d, path));

        const
            spath = path.split('/'),
            key = spath.shift();

        return key in data
            ? findPathInObject(data[key], spath.join('/'))
            : false;
    };

console.log(findPathInObject({ data: { school: 'yaba', age: 'tolu', message: 'true' }, time: 'UTC', class: 'Finals' }, "data/message", 3)); // true

console.log(findPathInObject({ data: { school: 'yaba', age: 'tolu', message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' }, "data/message/details/lastGreeting", 3)); // true

console.log(findPathInObject({ data: { school: 'yaba', age: 'tolu', names: ['darious'], area: [{ NY: true, BG: true }], message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' }, "data/area/NY", 3)); // true

Answer №2

discover

Inside this response, you'll find a forest that contains various levels of object and array nesting -

const forest =
  { data:
      { city: "lagos", population: "jane", message: "bar" }
  , elements:
      [ { name: "english" }, { name: "history" } ]
  , profound:
      [ { instance:
            [ { innermost: "hi" }
            , { innermost: "bye" }
            ]
        }
      ]
  }

Utilizing generators is an optimal strategy for tackling this kind of issue. Beginning with a general discover function that produces all possible outcomes for a specific path -

function discover (data, road)
{ function* loop (t, [k, ...more])
  { if (t == null) return
    if (k == null) yield t
    else switch (t?.constructor)
    { case Object:
        yield *loop(t[k], more)
      break
      case Array:
        for (const v of t)
          yield *loop(v, [k, ...more])
        break
    }
  }
  return loop(data, road.split("/"))
}
Array.from(discover(forest, "elements/name"))
Array.from(discover(forest, "profound/instance/innermost"))
Array.from(discover(forest, "a/b/c"))
[ "english", "history" ]
[ "hi", "bye" ]
[]

uncover

If you're looking for a function that retrieves one (the first) outcome, we can easily craft uncover. This approach is particularly effective since generators are capable of being paused or stopped. Once the initial result is identified, the generator will cease searching for additional results -

function uncover (data, road)
{ for (const result of discover(data, road))
    return result
}
uncover(forest, "data/city")
uncover(forest, "elements")
uncover(forest, "elements/name")
uncover(forest, "profound/instance/innermost")
uncover(forest, "a/b/c")
"lagos"
[ { name: "english" }, { name: "history" } ]
"english"
"hi"
undefined

validate

If you want to verify whether a specific path exists, we can create validate as a straightforward extension of uncover -

const validate = (data, road) =>
  uncover(data, road) !== undefined
validate(forest, "data/city")
validate(forest, "elements")
validate(forest, "profound/instance/innermost")
validate(forest, "a/b/c")
true
true
true
false

demonstration

Enlarge the code snippet below to confirm the outcomes in your own web browser -

function discover (data, road)
{ function* loop (t, [k, ...more])
  { if (t == null) return
    if (k == null) yield t
    else switch (t?.constructor)
    { case Object:
        yield *loop(t[k], more)
      break
      case Array:
        for (const v of t)
          yield *loop(v, [k, ...more])
        break
    }
  }
  return loop(data, road.split("/"))
}

function uncover (data, road)
{ for (const result of discover(data, road))
    return result
}

const forest =
  { data:
      { city: "lagos", population: "jane", message: "bar" }
  , elements:
      [ { name: "english" }, { name: "history" } ]
  , profound:
      [ { instance:
            [ { innermost: "hi" }
            , { innermost: "bye" }
            ]
        }
      ]
  }

console.log("uncover")
console.log(uncover(forest, "data/city"))
console.log(uncover(forest, "elements"))
console.log(uncover(forest, "elements/name"))
console.log(uncover(forest, "profound/instance/innermost"))
console.log(uncover(forest, "a/b/c"))
console.log("discover")
console.log(Array.from(discover(forest, "elements/name")))
console.log(Array.from(discover(forest, "profound/instance/innermost")))
console.log(Array.from(discover(forest, "a/b/c")))

Answer №3

Simply put, the path body.data.area[0].NY does not exist within body. However, the path body.data.area does exist. If you need to include objects within arrays as part of an object's path, the solution will be more intricate.

const path = "data/area/NY";
const path2 = "data/message/details/lastGreeting";
const notPath = "data/message/details/firstGreeting";
const body = {
  data: {
    school: 'yaba',
    age: 'tolu',
    names : ['darious'],
    area: {
       NY: true,
       BG: true
    },
    message: {
       content: 'now',
       details: {
          lastGreeting: true
       }
    }
  },
  time: 'UTC',
  class: 'Finals'
};

console.log(`${path} exists? ${exists(body, path) && `yes` || `no`}`);
console.log(`${path2} exists? ${exists(body, path2) && `yes` || `no`}`);
console.log(`${notPath} exists? ${exists(body, notPath) && `yes` || `no`}`);

function exists(obj, path) {
  const pathArray = path.split("/");
  while (pathArray.length) {
    const current = pathArray.shift();
    if (pathArray.length < 1 && current in obj) { return true; }
    if (current in obj) { return exists(obj[current], pathArray.join("/")); }
  }
  return false;
}

Answer №4

Verify this solution for accuracy, including when dealing with an array of objects.

const body = {
    data: {
        school: 'yaba',
        age: 'tolu',
        message: {
            content: 'now',
            details: {
                lastGreeting: true,
            },
        },
        area: [
            {
                NY: true,
                BG: true,
            },
        ],
    },
    time: 'UTC',
    class: 'Finals',
};
const spath1 = 'data/message';
const spath2 = 'data/message/details/lastGreeting';
const spath3 = 'data/area/NY';
const spath4 = 'data/area/NY/Test';

console.log(`${spath1}: `, isPathExists(body, spath1.split('/'), 0));
console.log(`${spath2}: `, isPathExists(body, spath2.split('/'), 0));
console.log(`${spath3}: `, isPathExists(body, spath3.split('/'), 0));
console.log(`${spath4}: `, isPathExists(body, spath4.split('/'), 0));

function isPathExists(data, pathArr, i) {
    const key = pathArr[i];

    if (Array.isArray(data)) {
        for (let value of data) {
            if (isObject(value)) return isPathExists(value, pathArr, i);
        }
    } else if (data.hasOwnProperty(key)) {
        if (key === pathArr[pathArr.length - 1]) return true;
        return isPathExists(data[key], pathArr, i + 1);
    } else return false;

    return true;
}
function isObject(a) {
    return !!a && a.constructor === Object;
}

Answer №5

Through collaboration with a colleague, we were able to devise a solution that is both simple and easily understandable, perfectly tailored to our requirements. While the initial answer using the yield implementation resolved the issue, we sought something more readable and user-friendly within the codebase. Our aim was to determine if a specific path exists in the object and retrieve its value.

To achieve this, we introduced a third parameter called returnValue, which defaults to returning the value. If we wish to alter this behavior, setting the return value to false allows the function to verify the existence of the specified path - returning true if found, or false otherwise.

This is the revised approach we settled upon:

const find = (path, data) => {
    if (Array.isArray(data)) {
        data = data[0];
    }
    for (const item in data) {
        if (item === path) {
            return data[item];
        }
    }
    return null;
};

const findPath = (fullPath, fullData, returnValue = true) => {

    const pathArray = fullPath.split('/');

    let findResult = fullData;
    for (const pathItem of pathArray) {
        findResult = find(pathItem, findResult);
        if (!findResult) {
            if (!returnValue) return false;
            return null;
        }
    }

    if (!returnValue) return true;
    return findResult;
};



const body = {
  name: 'mike',
  email: 1,
  data: {
    school: [
      {
        testing: 123
      }
    ]
  }
}

console.log(findPath('data/school/testing', body))

Answer №6

Check out this efficient solution using object-scan

.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="345b565e515740194757555a74d6578878582cc87d68c8989cb848800879fb4801585b3dc828f878886ff819bfe8c1d7b5888e97">[email protected]</a>/lib/index.min.js';

const data1 = { data: { school: 'yaba', age: 'tolu', message: 'true' }, time: 'UTC', class: 'Finals' };
const data2 = { data: { school: 'yaba', age: 'tolu', message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' };
const data3 = { data: { school: 'yaba', age: 'tolu', names: ['darious'], area: [{ NY: true, BG: true }], message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' };

const path1 = 'data/message';
const path2 = 'data/message/details/lastGreeting';
const path3 = 'data/area/NY';

const exists = (data, n) => objectScan([n.replace(/\//g, '.')], {
  useArraySelector: false,
  rtn: 'bool',
  abort: true
})(data);

console.log(exists(data1, path1));
// => true

console.log(exists(data2, path2));
// => true

console.log(exists(data3, path3));
// => true
</script>

Note: The developer behind object-scan is the same as in the original post.

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

What is the best way to find the most commonly used category for a product in a MongoDB collection?

I've been diving into MongoDB and encountered a challenge with my current task. I'm trying to figure out how to determine the most frequently used category in a collection. According to this JSON, the most used category is CIES. My goal is to dis ...

The process of binding two variables in the same select element

Here is the code snippet that I am working with: <div class="form-group "> <label for="field_type"><b>Type</b></label> <div class="input-icon right"> <select required class="form-control" ...

Using Firebase Firestore to fetch an array field from a particular document and then leveraging those array values for mapping other documents

In my database, I have user and group collections. Each document in the user collection corresponds to a user UID and contains an array field called "userGroups" which lists the groups that user belongs to, with each group being identified by the group&apo ...

Create a variable in Angular to determine the selected array

I have a JSON array and I am displaying the items in a select element. When an item is clicked, a new select is generated for any subarray items. If you follow this link, you can see exactly what I mean. Everything is working well so far, but I'm ha ...

Using both Ajax and a standard submit can be applied to a single form in jQuery

I need to implement a confirm step on one of my pages. Here's what I want to achieve: When the user clicks 'submit', an AJAX request is triggered, which performs the necessary action and then displays a confirmation dialog If the user ...

Implement authentication verification on all child endpoints within an express router

I have an express router set up and I want only authorized users to access its routes. I am currently using passport middleware. Instead of adding a check for req.user in every endpoint, is there a more efficient way to handle this? router.get("/", asyn ...

Safari encountering parsing date error

My Angular application is receiving date formats from a web service in the following format: myDate = "2020-03-05T08:00:00" This translates to the fifth of March, 2020 for me For Chrome, Firefox, and IE, the format is yyyy-mm-ddThh:mm:ss However, Safar ...

Creating an Eye-Catching Tumblr Landing Page

As I work on Tumblr, my goal is to create a landing page that features an "Enter" button directing users to the home page. After some research, I came across a code snippet that redirects the site to a /welcome page when placed in the index page. <scri ...

Having difficulties with JavaScript in Rails 4, receiving the error message: "undefined is not a function"

I have been using the gem chosen-rails (or chosen-sass-bootstrap-rails) in my Rails application. In my gemfile, I included the following line: gem 'chosen-sass-bootstrap-rails' Previously, everything was functioning correctly, but now I am enco ...

Background Services in the Moment

Currently in the process of developing a mobile application with the Ionic framework that utilizes Laravel 4 REST API for CRUD operations on a MySQL database. As per the requirements of the app, it needs to constantly communicate with the backend service t ...

Understanding the relationship between csv and json array formats, along with the process of converting them into a json array using Node.js

Greetings! I have been searching for quite some time and have not been able to find the desired result. I am unsure of what a CSV file would look like with the following JSON array: [ { email: "<a href="/cdn-cgi/l/email-protection" class="__cf_email_ ...

How can one retrieve an HTTP response by utilizing jQuery or JavaScript?

Having recently transitioned to web programming from a background in Java, I am now facing challenges in retrieving an HTTP response by accessing a specific URL. Despite referring to resources like the W3C schools, I have yet to achieve the desired result. ...

Endless AngularJS loop using ng-view

I recently started experimenting with AngularJS for a new project I am working on, but I have encountered a problem when dealing with routes and views. For the sake of simplicity, I have minimized this example to its basic form, yet the issue persists. Th ...

Using the JQuery `append()` method, I aim to generate a pair of Bootstrap columns by iterating through an associative array's keys

I've been struggling to design a form that includes labels and inputs. I have an associative array with labels as keys and corresponding values as array objects. My goal is to create a bootstrap horizontal form with two columns (col-6) each. However, ...

Utilizing an array in a PHP URL and incorporating it into JavaScript operations

In this PHP file, I am working on validating only numeric input for text-boxes with the ids "Mobile" and "Home": $elementids = array("Mobile","Home"); $serialized = rawurlencode(serialize($elementids)); $testvar='validate-nums.php?elementids='. ...

Avoid creating empty blocks in ASP.NET MVC

I'm working on some back-end code Take a look at the code snippet below [HttpGet] public ActionResult GetQuestions() { var _id = TempData["ID"]; var questBlock = db.QuestionBlocks .Where(x => x.Interview_Id == ...

Can a sophisticated text editor be utilized without a content management system?

Many website builders utilize rich text editors as plugins to enhance content creation, such as in CMS platforms like Joomla and WordPress. However, can these same editors be easily integrated into a custom website built from scratch using just HTML, PHP ...

What could be causing the issue with my dynamic sitemap.xml file not functioning properly?

I have encountered an issue with creating a dynamic sitemap in my Next.js app. Despite following the instructions in the Next.js documentation and placing a sitemap.js file inside my app directory, I am seeing a 404 error when trying to access http://local ...

nuxt-auth is experiencing difficulties retrieving token information through the refresh provider

I'm currently facing challenges with the login functionality in Nuxt 3. To handle user authentication, I've installed the package @sidebase/nuxt-auth. Below are my configurations set in the file nuxt.config.ts: auth: { globalAppMiddleware: t ...

Converting an associative array in JavaScript to a string map in C++ with Swig: A step-by-step guide

In a situation quite reminiscent of this query, I am aiming to wrap a function using SWIG that accepts a map of strings to strings: void foo(std::map<std::string, std::string> const& args); Creating an alias for the map suffices for Python: na ...