Exploring nested JavaScript attributes using a personalized, dynamic approach

When working with this JavaScript object, the goal is to access its nested properties dynamically using a method that begins with 'get' followed by a CamelCased attribute name (provided that the attribute is defined in the initial object).

While I am aware that achieving this may involve utilizing a Proxy object along with recursion, any additional insights or suggestions are greatly appreciated.

Thank you for your assistance!

function Order() {
    this.no = 'n1234';
    this.info = {
        customer: {
            ID: 'c1234'
            address: {
                //...
                shiping: {
                    // ...
                }
            }
        }
    }
}

var order = new Order();

// The expected result should be:
// order.no = order.getNo()
// order.info = order.getInfo()
// order.info.customer or order.info.getCustomer() or order.getInfo().customer or order.getInfo().getCustomer()
// and so on for all nested attributes

I attempted to implement this functionality with the following code snippet, but it appears not to work as intended for accessing nested properties.

function createProxy(obj) {
  return new Proxy(obj, {
    get(target, prop) {
      if (typeof target[prop] === 'object' && target[prop] !== null) {
        return createProxy(target[prop]);
      }
      if (prop.startsWith('get')) {
        const propName = prop.charAt(3).toLowerCase() + prop.slice(4);
        if (target[propName] !== undefined) {
          return () => target[propName];
        }
      }
      return target[prop];
    },
  });
}

function Order() {
  const properties = {
    no: 'n1234',
    info: {
      customer: {
        ID: 'c1234'
      }
    }
  };

  return createProxy(properties);
}

var order = new Order();

// order.getInfo().customer - { ID: 'c1234' }
// order.getInfo().getCustomer() - TypeError: order.getInfo().getCustomer is not a function"

Answer №1

If a few adjustments are made, it would greatly improve the code, particularly by ensuring that getXXX() calls also return a proxy:

const createProxy = (inputObj) => new Proxy(inputObj, {
  get(target, key) {
    const result = () => target[key]?.__proto__ === Object.prototype ? createProxy(target[key]) : target[key];
    return key.startsWith?.('get') && (key = key[3].toLowerCase() + key.slice(4)) ? result : result();
  },
});

function NewOrder() {
  const props = {
    number: 'N5678',
    details: {
      client: {
        ID: 'C5678',
        name: 'Good client'
      }
    }
  };

  return createProxy(props);
}

var newOrder = new NewOrder();

console.log(newOrder.getDetails());
console.log(newOrder.getDetails().getClient().getName());
console.log(newOrder.details.getClient().ID);

Answer №2

Below is the solution that has been successfully implemented. Huge thanks to @Barmar and Alexander Nenashev

/**
 * @param {Object} obj
 * @returns {Proxy} obj
 */
function createProxy(obj) {
    return new Proxy(obj, {
        get(target, prop) {
            function outputFunc() {
                if (Object.getPrototypeOf(target[prop]) === Object.prototype) {
                    return createProxy(target[prop]);
                } else {
                    return target[prop];
                }
            }

            if (!prop.startsWith('get')) {
                return outputFunc();
            }

            var property = prop[3] + prop.slice(4);

            if (property.valueOf().toUpperCase() === property.valueOf()) {
                prop = property;
                return outputFunc;
            } else {
                prop = prop[3].toLowerCase() + prop.slice(4);
                return outputFunc
            }
        },
    });
}

function Order() {
    this.no = 'n1234';
    this.info = {
        customer: {
            ID: 'c1234',
            address: {
                shipping: {
                    ID: 's4354654'
                }
            }
        }
    }
    
    return createProxy(this);
}

var order = new Order();

console.log(order.no) // "n1234"
console.log(order.getNo()) // "n1234"
console.log(order.info.getCustomer().getID()) // "c1234"
console.log(order.getInfo().customer.ID) // "c1234"
console.log(order.info.customer.getAddress().getShipping().ID) // "s4354654"
console.log(order.getInfo().getCustomer().address.getShipping().getID()) // "s4354654"

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

Error Encountered: "JSON Post Failure in ASP.net MVC resulting in 500

Whenever I attempt to send a variable to JSON on ASP.net MVC, I encounter the following error: jquery-2.2.3.min.js:4 GET http://localhost:58525/Order/GetAddress/?userid=42&email=asandtsale%40gmail.com 500 (Internal Server Error) This is my controller ...

Vue component encounters issue with exporting JavaScript functions

I decided to streamline my code by moving some functions into a JavaScript file like this: [...] function changeToEditView(reportId) { let pathEdit="/edit/"+reportId; this.$router.push({ path: pathEdit, replace: true }); } [...] export {c ...

What is the best way to encode a GLTF file without compromising the encoding of other meshes and textures?

Currently, I am working on a fascinating web AR app that enables users to don GLTF head models. (You can check it out at ) To ensure optimal lighting of the GLTF model, I have implemented renderer.outputEncoding = THREE.sRGBEncoding, which has been very ef ...

Allowing the primary content to span the entire width of the mobile screen

I have scoured various forums in search of answers, but unfortunately, I haven't found a solution that fits my specific query. I am looking to ensure that the .main-content (article text and images) occupies the full width of the mobile screen without ...

The Ultimate Guide for Formatting JSON Data from Firebase

I'm facing an issue with parsing JSON data returned by Firebase. Here is the JSON snippet: { "-JxJZRHk8_azx0aG0WDk": { "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="cda6a68daaa0aca4a1e3aea2a0">[email&# ...

Flickity remains in plain sight on desktop devices

I am trying to hide the flickity slider on desktop and larger devices. I have followed the instructions in the documentation, but for some reason, it's not working as expected. Here is how the div looks: <div class="w-full flex pl-4 pb-16 overflo ...

When trying to use setInterval () after using clearInterval () within an onclick event, the functionality seems

Can anyone assist me with an issue I am encountering while using the setInterval() function and then trying to clear it with clearInterval()? The clearInterval() works fine, but the automatic functionality of li elements with a specific class suddenly stop ...

Exploring the functionality of the $.each jQuery iterator. Can someone clarify the distinctions between these two methods?

Having vertices as an array of google.maps.LatLng objects means that they should return latlng points. The first code snippet works perfectly fine, however, I encounter issues when using the second one. // Iterate over the vertices. for (var index =0; ind ...

Creating dynamic routes with Map in ReactJS: a beginner's guide

In the sample code below, I have added a column to the children property that includes my component. I am attempting to pass this to my Route element using map in order to dynamically populate the React Route. navLink.js export const navLinks = [ { ...

Trigger a notification based on the selected choice

Here is some sample HTML code: <div id="hiddenDiv" style="display: none;"> <h1 id="welcomehowareyou">How are you feeling today?</h1> <select name="mood" id="mood"> <option value="" disabled selected>How are you feeling?</o ...

Encountering NPM Abortion Issue in Node.js

I am a beginner in node.js and have successfully installed it. However, when I try to initialize npm, I encounter an error that causes it to abort. My system has high memory capacity with 32GB RAM and 1TB HD. Although the 'npm -v' command works ...

What sets local Node package installation apart from global installation?

My curiosity sparked when I began the process of installing nodemon through npm. Initially, I followed the recommended command and noticed the instant results displayed on the right side of my screen: npm i nodemon This differed from the installation ins ...

Rendering an Image File from Database Using React JS and Node JS

I am encountering a problem with displaying an image file from my mongodb database on my Profile and EditProfile component. The issue arises when I upload the file using the Editprofile component - it saves to the database successfully. However, when I try ...

"Discover the power of Algolia's docSearch feature

Currently, I am working on integrating Algolia DocSearch into my docusaurus project. After obtaining the api key and api id from Algolia, I am unsure of the next steps to take. I would appreciate guidance on the necessary procedures that need to be followe ...

The Vuetify data-table header array is having trouble accepting empty child associations

My Vuetify data-table relies on a localAuthority prop from a rails backend. Everything runs smoothly until an empty child association (nested attribute) is passed, specifically 'county': <script> import axios from "axios"; exp ...

Using bufferCount in Rxjs: Retrieving the latest values

Is there a method to access the most recent values emitted while using bufferCount(x), even if the buffer size does not reach x? For example, in the code snippet below, only [0, 1] is printed. I would like the output to also include [2] under certain circ ...

Enhancing Performance with Web Workers in Angular CLI

Lately, I've been tackling a challenging issue with my Angular app - heavy computation on the client side leading to UI blockage. My solution? Utilizing Web Workers in my Angular CLI project to separate UI tasks into one thread and heavy processing in ...

Seeking the perfect message to display upon clicking an object with Protractor

Currently, I am using Protractor 5.1.1 along with Chromedriver 2.27. My goal is to make the script wait until the message "Scheduling complete" appears after clicking on the schedule button. Despite trying various codes (including the commented out code), ...

Unable to reference a property or method in Vue.js and Vuetify due to an issue with the <v-btn-toggle> button causing an error

Just started using vuetify and exploring the <v-btn-toggle> component for the first time. I'm trying to implement a toggle feature to filter my user list based on employee, manager, or admin types... Check out a snippet of my code below: <v ...

Run custom JavaScript code dynamically within a webpage

What is the most effective way to use Java to automatically open a web page, run some JavaScript to complete and submit a form, and analyze the outcome? ...