Home > Software design >  update object value by matching id within a deeply nested object in Javascript?
update object value by matching id within a deeply nested object in Javascript?

Time:02-26

I have this Javascript object:

{
  title: "Securities Finance Trade Entry",
  children: [
    {
      containerType: "Tabs",
      children: [
        {
          title: "Common",
          children: [
            {
              containerType: "Row", 
              children: [
                {
                  input: "ComboBox",
                  label: "Trade Type",                  
                  options: ["Repo", "Buy/Sell", "FeeBased"],
                  value: "FeeBased"
                },
                {
                  input: "ComboBox",
                  label: "Direction",
                  options: ["Loan", "Borrow"],
                  value: "Borrow"
                }
              ]
            },
            {
              containerType: "Row",
              children: [
                {
                  containerType: "Column",
                  children: [
                    {
                      containerType: "Row",
                      children: [
                        {
                          input: "Text",
                          label: "Book",
                      value: "test"
                        },
                        {
                          input: "Text",
                          label: "Counterparty",
                      value: "test"
                        }
                      ]
                    },
                    {
                      containerType: "Row",
                      children: [
                        {
                          input: "Date",
                          label: "StartDate",
                      value: "10/02/2021"
                        },
                        {
                          input: "Date",
                          label: "EndDate",
                      value: "10/02/2021"
                        }
                      ]
                    },
                    {
                      containerType: "Row",
                      children: [
                        {
                          input: "Text",
                          label: "Security",
                      value: "test"
                        },
                        {
                          input: "Numeric",
                          label: "Quantity",
                      value: "test"
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
} 

given a specific label I need to find the object with that label and then replace the value, the issue I have is that this format could be more nested and I cant look at any specific level, I need to look at all objects at all levels then just replace the value in the one where the label matches. I have tried recursion but can't get it to work, please can anyone advise?

EDIT:

This is my attempt but it fails badly :)

    const deepReplace = (obj, id, value) => {        
        if  (obj.children) {
            obj = obj.children.map((childObj) => {              
                if (typeof childObj === "object") {
                   deepReplace(childObj, id, value)
                }
            })
            return obj;
        };                
        if (obj.label === id) {
          obj['value'] = value;
          return obj
        };
     };

const newObj = deepReplace(myObj, 'TradeType', 'Repo')

CodePudding user response:

This problem seems to be a good candidate for recursion. Since we have to do a repeated operation multiple times and we are not sure how many.

  1. Base case: if label is there and is same as required then change the value
  2. If label does not exist or is not same then check if the property children exists and then iterate over it and repeat 1 and 2.

// passed (obj, "Book", "changed")
const replaceValueOfTheLabel = (obj, label, value) => {
  if (obj.label === label) {
    return obj.value = value;
  }

  if (obj.children)
    for (const ob of obj.children) {
      replaceValueOfTheLabel(ob, label, value)
    }

}

const obj = {
  title: "Securities Finance Trade Entry",
  children: [{
    containerType: "Tabs",
    children: [{
      title: "Common",
      children: [{
          containerType: "Row",
          children: [{
              input: "ComboBox",
              label: "Trade Type",
              options: ["Repo", "Buy/Sell", "FeeBased"],
              value: "FeeBased"
            },
            {
              input: "ComboBox",
              label: "Direction",
              options: ["Loan", "Borrow"],
              value: "Borrow"
            }
          ]
        },
        {
          containerType: "Row",
          children: [{
            containerType: "Column",
            children: [{
                containerType: "Row",
                children: [{
                    input: "Text",
                    label: "Book",
                    value: "test"
                  },
                  {
                    input: "Text",
                    label: "Counterparty",
                    value: "test"
                  }
                ]
              },
              {
                containerType: "Row",
                children: [{
                    input: "Date",
                    label: "StartDate",
                    value: "10/02/2021"
                  },
                  {
                    input: "Date",
                    label: "EndDate",
                    value: "10/02/2021"
                  }
                ]
              },
              {
                containerType: "Row",
                children: [{
                    input: "Text",
                    label: "Security",
                    value: "test"
                  },
                  {
                    input: "Numeric",
                    label: "Quantity",
                    value: "test"
                  }
                ]
              }
            ]
          }]
        }
      ]
    }]
  }]
}

replaceValueOfTheLabel(obj, "Book", "changed");

console.log(obj);

CodePudding user response:

If you're interested in an answer that doesn't mutate your original data but returns a copy with the appropriate values replaced, it might look like this:

const deepReplace = (label, value) => (obj) => Object (obj) == obj
  ? { ... obj, 
      ... (obj .label == label ? {value} : {}), 
      ... (obj .children ? {children: obj .children .map (deepReplace (label, value))} : {})
    }
  : obj

const input = {title: "Securities Finance Trade Entry", children: [{containerType: "Tabs", children: [{title: "Common", children: [{containerType: "Row", children: [{input: "ComboBox", label: "Trade Type", options: ["Repo", "Buy/Sell", "FeeBased"], value: "FeeBased"}, {input: "ComboBox", label: "Direction", options: ["Loan", "Borrow"], value: "Borrow"}]}, {containerType: "Row", children: [{containerType: "Column", children: [{containerType: "Row", children: [{input: "Text", label: "Book", value: "test"}, {input: "Text", label: "Counterparty", value: "test"}]}, {containerType: "Row", children: [{input: "Date", label: "StartDate", value: "10/02/2021"}, {input: "Date", label: "EndDate", value: "10/02/2021"}]}, {containerType: "Row", children: [{input: "Text", label: "Security", value: "test"}, {input: "Numeric", label: "Quantity", value: "test"}]}]}]}]}]}]}

console .log (deepReplace ('Counterparty', '*** New value ***') (input))
.as-console-wrapper {max-height: 100% !important; top: 0}

We test if our input is an object. If not, we simply return it. If it is, we create a new object by including all the root properties of the object, replacing a value property if we have the right label, and then recurring on the children nodes.

There are all sorts of benefits to immutable data. I would recommend adopting it when you can.

  • Related