简体   繁体   English

在打字稿中递归更新嵌套对象中的值

[英]Update values in nested object recursively in typescript

We need to update the object in nested objects using recursion in typescript .Need to add extra properties to the object which is in a nested object .我们需要在打字稿中使用递归更新嵌套对象中的对象。需要向嵌套对象中的对象添加额外的属性。 Nesting can change overtime and so recursion will only work Below is the input data :嵌套会随着时间的推移而改变,因此递归只会起作用 下面是输入数据:

[
  {
    "headerName": "Group 1",
    "children": [
      {
        "field": "G1-C1"
      },
      {
        "field": "G1-C2"
      }
    ]
  },
  {
    "headerName": "Group 2",
    "children": [
      {
        "headerName": "G2 - C1",
        "children": [
          {
            "field": "G2 - C1-C1"
          },
          {
            "field": "G2 - C1-C2"
          }
        ]
      },
      {
        "field": "G2-C2"
      },
      {
        "field": "G2-C3"
      }
    ]
  },
  {
    "headerName": "Group3",
    "children": [
      {
        "field": "G3-C1"
      },
      {
        "field": "G3-C2"
      }
    ]
  }
]

which needed to be transformed as :需要转换为:

[
  {
    "headerName": "Group 1",
    "children": [
      {
        "field": "G1-C1",
        "visible": true,
        "width": 200,
        "headerName": "Group1"
      },
      {
        "field": "G1-C2",
        "visible": true,
        "width": 200,
        "headerName": "Group1"
      }
    ]
  },
  {
    "headerName": "Group 2",
    "children": [
      {
        "headerName": "G2 - C1",
        "children": [
          {
            "field": "G2 - C1-C1",
            "width": 200,
            "headerName": "Group2-C1"
          },
          {
            "field": "G2 - C1-C2",
            "width": 200,
            "headerName": "Group2-C1"
          }
        ]
      },
      {
        "field": "G2-C2",
        "width": 200,
        "headerName": "Group2"
      },
      {
        "field": "G2-C3",
        "width": 200,
        "headerName": "Group2"
      }
    ]
  },
  {
    "headerName": "Group3",
    "children": [
      {
        "field": "G3-C1",
        "width": 200,
        "headerName": "Group3"
      },
      {
        "field": "G3-C2",
        "width": 200,
        "headerName": "Group3"
      }
    ]
  }
]

Tried several ways but was not able to find a way .尝试了几种方法,但无法找到方法。 It would be great help of there is any quick way to find a solution for this problem .如果有任何快速方法可以找到此问题的解决方案,那将是非常有帮助的。 This below method works but not sure if its is correct .下面的方法有效,但不确定它是否正确。

          formatData(columns: any) {     
            columns.forEach((i: any,index) => {
              if (i.hasOwnProperty('children')) {       
                this.formatData(i.children);
              } else {       
                columns[index] = {...{ field : i.field, headerName: 
                i.field, sortable: true, hide: false }};                    
              }      
            });            
          } 
           

FIrst of all we need to define type of our data structure:首先,我们需要定义数据结构的类型:


type WithChildren = {
  headerName: string,
  children: DataStructure[]
}

type WithoutChildrenInput = {
  field: string,
}


type WithoutChildrenOutput = {
  field: string,
} & Pick<WithChildren, 'headerName'>

type DataStructure = WithChildren | WithoutChildrenInput

type DataStructureOutput = WithChildren | WithoutChildrenOutput

Then we can define our logic:然后我们可以定义我们的逻辑:

const fieldOutput = (
  field: string,
  headerName: string
) => ({
  field,
  headerName,
  visible: true,
  width: 200,
})

const childrenOutput = (headerName: string, children: DataStructure[]) => (
  { headerName, children: builder(children, headerName) }
)

const withChildren = <Obj, Prop extends string>(obj: DataStructure)
  : obj is WithChildren =>
  Object.prototype.hasOwnProperty.call(obj, 'children');

const builder = (data: DataStructure[], headerName = ''): DataStructureOutput[] =>
  data.reduce<DataStructureOutput[]>((acc, elem) =>
    withChildren(elem)
      ? [...acc, childrenOutput(elem.headerName, elem.children)]
      : [...acc, fieldOutput(elem.field, headerName)], [])

const result = builder(data)

I have created two helpers: childrenOutput and fieldOutput .我创建了两个助手: childrenOutputfieldOutput

fieldOutput - just creates a plain object of field entity. fieldOutput - 只是创建一个字段实体的普通对象。 nothing special没什么特别的

childrenOutput - generates expected data structure with children and calls under the hood builder function. childrenOutput - 生成带有子项的预期数据结构,并在 hood builder函数下调用。

withChildren - is a user defined typeguard which helps narrow the type withChildren - 是用户定义的 typeguard ,有助于缩小类型

You may think that it is a bad practice to call function before you define it.您可能认为在定义函数之前调用函数是一种不好的做法。 You can declare builder with function keyword or pass third argument to childrenOutput like here:您可以使用function关键字声明builder或将第三个参数传递给childrenOutput如下所示:

const childrenOutput = (headerName: string, children: DataStructure[], callback: (data: DataStructure[], headerName: string) => DataStructureOutput[]) => (
  { headerName, children: builder(children, headerName) }
)

It is up to you.它是由你决定。

Whole code:全码:



type WithChildren = {
  headerName: string,
  children: DataStructure[]
}

type WithoutChildrenInput = {
  field: string,
}


type WithoutChildrenOutput = {
  field: string,
} & Pick<WithChildren, 'headerName'>

type DataStructure = WithChildren | WithoutChildrenInput

type DataStructureOutput = WithChildren | WithoutChildrenOutput

const data: DataStructure[] = [
  {
    "headerName": "Group 1",
    "children": [
      {
        "field": "G1-C1"
      },
      {
        "field": "G1-C2"
      }
    ]
  },
  {
    "headerName": "Group 2",
    "children": [
      {
        "headerName": "G2 - C1",
        "children": [
          {
            "field": "G2 - C1-C1"
          },
          {
            "field": "G2 - C1-C2"
          }
        ]
      },
      {
        "field": "G2-C2"
      },
      {
        "field": "G2-C3"
      }
    ]
  },
  {
    "headerName": "Group3",
    "children": [
      {
        "field": "G3-C1"
      },
      {
        "field": "G3-C2"
      }
    ]
  }
]

const fieldOutput = (
  field: string,
  headerName: string
) => ({
  field,
  headerName,
  visible: true,
  width: 200,
})

const childrenOutput = (headerName: string, children: DataStructure[], callback: (data: DataStructure[], headerName: string) => DataStructureOutput[]) => (
  { headerName, children: builder(children, headerName) }
)

const withChildren = <Obj, Prop extends string>(obj: DataStructure)
  : obj is WithChildren =>
  Object.prototype.hasOwnProperty.call(obj, 'children');

const builder = (data: DataStructure[], headerName = ''): DataStructureOutput[] =>
  data.reduce<DataStructureOutput[]>((acc, elem) =>
    withChildren(elem)
      ? [...acc, childrenOutput(elem.headerName, elem.children, builder)]
      : [...acc, fieldOutput(elem.field, headerName)], [])

const result = builder(data)

console.log({ result })

Playground 操场

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM