通过匹配 Javascript 中深层嵌套对象中的 id 来更新对象值?

发布于 2025-01-10 01:09:41 字数 3220 浏览 1 评论 0原文

我有这个 Javascript 对象:

{
  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"
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
} 

给定一个特定的标签,我需要找到带有该标签的对象,然后替换该值,我遇到的问题是这种格式可能更加嵌套,我无法查看任何特定级别,我需要查看在所有级别的所有对象中,只需替换标签匹配的对象中的值即可。我尝试过递归,但无法让它工作,请问有人可以建议吗?

编辑:

这是我的尝试,但失败得很严重:)

    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')

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')

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

定格我的天空 2025-01-17 01:09:41

这个问题似乎很适合递归。因为我们必须多次重复操作,而且我们不确定有多少次。

  1. 基本情况如果标签存在并且与所需的相同,则更改值
  2. 如果标签不存在或不相同,则检查属性children 存在,然后迭代它并重复 1 和 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);

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);

猥琐帝 2025-01-17 01:09:41

如果您对不会改变原始数据但返回替换了适当值的副本的答案感兴趣,它可能如下所示:

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}

我们测试我们的输入是否是一个对象。如果没有,我们只需将其退回即可。如果是,我们通过包含该对象的所有根属性来创建一个新对象,如果我们有正确的标签,则替换value属性,然后在< code>children 节点。

不可变数据有各种各样的好处。我建议您尽可能采用它。

更新:代码解释

评论表明这很难阅读。这是我试图解释的。

const deepReplace = (label, value) => (obj) => /* ... */

首先,我们柯里化该函数,传递labelvalue来获取另一个函数,该函数接受obj并返回一个进行更改的对象。这在内部(我们稍后将对 deepReplace (label, value) 的引用传递给 map)和外部都很有用,因为我们现在只需传递标签和值即可得到一个我们可以应用于许多不同对象的函数。

然后我们开始条件运算符(三元)。我们使用Object (obj) === obj来测试输入参数是否是一个对象。有很多方法可以对此进行测试,包括 typeof 和对值调用 Object.prototype.toString()。我发现这个非常强大。

如果它不是一个对象,那么我们点击最后一行 : obj,并返回原始值。如果它是一个对象,我们返回一个通过三个步骤创建的对象:

    ... obj,

这里我们只是将原始对象扩展到新对象中。这是浅克隆的一种形式。

    ... (obj .label == label ? {value} : {}), 

在这里,我们测试对象的 label 属性是否与我们的目标匹配。如果是,我们将 {value}(这是 {value: value} 的现代简写)传播到我们的对象中。如果没有,我们将传播一个空对象,从而不添加任何属性。

    ... (obj .children ? {children: obj .children .map (deepReplace (label, value))} : {})

在这里,我们处理孩子。如果我们的节点没有 children 属性,我们将传播一个空对象。如果确实如此,我们将递归调用映射到它们之上的同一函数,并传播到其 children 属性为结果的对象中。这里柯里化很有帮助,因为如果我们的函数看起来像 deepReplace = (label, value, obj) => ,我们只需调用 map (deepReplace (label, value)) 即可。 /* ... */,那么这里我们必须调用map ((item) => deepReplace (label, value, item))。这没什么大不了的,但这种方式感觉更干净。

因此,在较旧的 JS 中,我们可能会编写如下所示的等效代码:

const deepReplace = (label, value) => (obj) => {
  if (Object (obj) !== obj) {
    return obj
  }

  const newObject = {...obj}

  if (obj .label == label) {
    newObject .value = value
  }

  if ('children' in obj) {
    newObject .children = obj .children .map (deepReplace (label, value))
  }
  return newObject
}

这本质上是等效的。我发现新的语法更有启发性,但两者都可以。


我们可能应该处理数组的情况,这会给实现增加一点点,(未经测试的)版本可能如下所示:

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

这里我们首先检查数组,如果是的话,map 结果,就像我们对孩子所做的那样。

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.

Update: Code Explanation

A comment suggested that this was challenging to read. Here's my attempt to explain it.

const deepReplace = (label, value) => (obj) => /* ... */

First, we curry the function, passing label and value to get back another function that takes obj and returns an object with the change made. This is useful both internally (we later will pass a reference to deepReplace (label, value) to map) and externally, as we can now just pass the label and value and get a function we can apply to many different objects.

We then begin a conditional operator (ternary). We test whether the input parameter is an object, using Object (obj) === obj. There are many ways to test this, including typeof and calling Object.prototype.toString() on the value. I find this one quite robust.

If it is not an object, then we hit the last line : obj, and return the original value. If it is an object, we return an object created out of three steps:

    ... obj,

Here we just spread the original object into our new one. This is a form of shallow cloning.

    ... (obj .label == label ? {value} : {}), 

Here we test if the object's label property matches our target. If it does, we spread {value}, which is modern shorthand for {value: value} into our object. If it doesn't, we spread an empty object, thereby adding no properties.

    ... (obj .children ? {children: obj .children .map (deepReplace (label, value))} : {})

And here, we handle children. If our node has no children property, we spread an empty object. If it does, we map a recursive call to the same function over them, and spread in an object whose children property is the result. Here the currying helps, as we can just call map (deepReplace (label, value)) If our function looked like deepReplace = (label, value, obj) => /* ... */, then here we would have to call map ((item) => deepReplace (label, value, item)). It's not a big deal, but this way feels cleaner.

So in older JS, we might have written the equivalent code like this:

const deepReplace = (label, value) => (obj) => {
  if (Object (obj) !== obj) {
    return obj
  }

  const newObject = {...obj}

  if (obj .label == label) {
    newObject .value = value
  }

  if ('children' in obj) {
    newObject .children = obj .children .map (deepReplace (label, value))
  }
  return newObject
}

That would have been essentially equivalent. I find the newer syntax more enlightening, but either would work.


We probably should have handled the array case, and that would add a little bit to the implementation, and an (untested) version might look like this:

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

Here we check for an array first, and if so, map the results, as we do for children.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文