如何从对象中递归删除子对象? [英] How to remove children objects recursively from object?

查看:59
本文介绍了如何从对象中递归删除子对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究解决方案

每当用户单击与被点击的行相关的展开数据时,我都会创建基本的树型表.

I have created basic tree kind of table whenever user click on expand data related to clicked row will appear under it based on row data

我已经实现了最多可扩展至N个嵌套级别的基本功能.

I have achieved basic functionality of expand/collapse upto N nested levels.

但是我只遇到一个问题,所以基本上所有行都有基于具有多个值的数组的条件扩展按钮

But i am stuck with only one problem, so basically all row have conditional expand button based on array having multiple values

让我们说它是具有3个县,市,州的拆分数组

Lets say it is split array having 3 entries county,city,state

默认加载的数据将从api中获取,现在我必须检查数组是否有可用的拆分!如果可以,则我可以使展开按钮可见

Default loaded data will be fetched from api, now i have to check array that is there any split available! if yes than i make expand button visible

考虑这种情况

const split = ["country","city","state"]

这是Ui的样子

+ Data_1
+ Data_2

点击按钮+新数据表行将基于下一个可用的拆分呈现,在我们的案例中是国家/地区,因此视觉表示将类似于

on click of button + new data table row will be rendered based on next split available in our case it is country so visual representation will be like

- Data_1
   Country_1
   Country_2
+ Data_2

此处的国家/地区没有展开按钮,因为用户尚未添加下一个拆分,让我们添加城市,并假设用户点击了Country_1,因此数据将类似于

Here country does not have expand button as user have not added next split yet, lets add city, and assume user have clicked Country_1 so data will be like

    - Data_1
       - Country_1
           City_1
           City_2
       + Country_2
    + Data_2

我的解决方案运行良好,直到该级别现在允许用户从拆分中删除了国家,应该删除了国家和城市的所有节点,并且-data_1的图标应更改为+

My solution works fine till this level now lets say user have removed country from split that all nodes of country and city should be removed and - icon of data_1 should be changed to +

这是我的代码

import React, {useState, useEffect, useRef, Fragment} from "react";
  import _ from "lodash";
  import axios from "axios";

  class TableRowData extends React.Component {
    state = {
      showIcon: false,
      selection: [],
      data: [],
      splitOption: ["campid"]
    };
    constructor(props) {
      super(props);
    }

    componentDidMount() {
      const checkIfSplitExistOnMount = (currentSplit) => {
        const i = _.findIndex(this.state.splitOption, function(el) {
          return el === currentSplit;
        });

        if (this.state.splitOption[i + 1]) {
          return this.state.splitOption[i + 1];
        } else {
            return null;
        }
      }
      const getReportData = () => {
        axios.get("https://jsonplaceholder.typicode.com/users?_start=0&_limit=1").then((res) => {
          const rowData = res.data.map((row) => {
            row.name = this.state.splitOption[0];
            row.isExpanded = false;
            row.currentSplit = this.state.splitOption[0];
            row.nextSplit = checkIfSplitExistOnMount(this.state.splitOption[0])
            row.parentId = 0;
            row.isVisble = true;
            //console.log(row)
            return row;
          });
          this.setState({
            data: rowData
          }, () => { //console.log(this.state.data)
          });
        });
      }
      getReportData()
    }

    render() {
      // update state function
      const updateState = () => {
        this.setState({
          data: [...this.state.data],
          splitOption: [...this.state.splitOption],
          selection: [...this.state.selection],
        }, () => {})
      }

      // recusively update parent and child
      const recursion = (obj) => {
         let row = obj;
         row.isExpanded = row.isExpanded;
         row.currentSplit = row.currentSplit;
         row.nextSplit = checkIfSplitExist(row.currentSplit)

         if (row.children && row.children.length > 0) { // check if has children
            row.children.forEach(v => { // if has children do the same recursion for every children
              recursion(v);
            });
         }
         return row; // return final new object
       }

       const recursionDel = (obj,split) => {
           var row = obj;
           row.currentSplit = row.currentSplit;
           row.nextSplit = checkIfSplitExist(row.currentSplit)
           if (row.children && row.children.length > 0) { // check if has children
             row.children.forEach(v => { // if has children do the same recursion for every children
               recursionDel(v);
             });
          }
          return row; // return final new object
        }

      // function to check if next split is there or not if there than return nextsplit
      const checkIfSplitExist = (currentSplit) => {
        const i = _.findIndex(this.state.splitOption, function(el) {
          return el === currentSplit;
        });
        if(i !== -1) {
          if (this.state.splitOption[i + 1]) {
            return this.state.splitOption[i + 1];
           } else {
            return null;
          }
        }

      }

      // recursive update whenever split added
      const recursiveUpdate = (data) => {
        const prevData = [...data];
        return prevData.map((row) => {
          const updatedData =  recursion(row);
          return row;
        });
      }

      // function to delete child and parent node recursively
      const recursiveDelete = (data,split) => {
        const prevData = [...data];
        return prevData.map((row) => {
          const data =  recursionDel(row,split);
          return row;
        });
      }

      const addNewSplit = (split) => {
        const i = _.findIndex(this.state.splitOption, function(el) {
          return el === split;
        });
        if(i === -1) {
            this.setState(
              {
                splitOption:[...this.state.splitOption,split]
              },
              ()=>{
                var rowData = recursiveUpdate(this.state.data)
                this.setState({data:rowData})
              }
          );
        } else {
          const prevData = [...this.state.splitOption];
          var index = prevData.indexOf(split);
          prevData.splice(index,1)
          if(index!==-1) {
            this.setState(
                {
                    splitOption:prevData
                },
                ()=> {
                  var rowData = recursiveDelete(this.state.data,split)
                  this.setState({data:rowData})
                }
              )
          }
        }

      }

      // add lazyload expand data
      const ExpandableTableRow = ({rows}) => {

        const expandRow = (row) => {
          row.children = [
            {
              id: "_" + Math.random().toString(36).substr(2, 5),
              name: row.id + "_" + row.nextSplit,
              isExpanded: false,
              parentId: row.id,
              currentSplit: row.nextSplit,
              nextSplit: checkIfSplitExist(row.nextSplit),
              isVisble:true
            }, {
              id: "_" + Math.random().toString(36).substr(2, 5),
              name: row.id + "_" + row.nextSplit,
              isExpanded: false,
              parentId: row.id,
              currentSplit: row.nextSplit,
              nextSplit: checkIfSplitExist(row.nextSplit),
              isVisble:true
            }
          ];
          row.isExpanded = true;
          updateState();
        };

        // call whenever - click
        const collapseRow = (row) => {
          delete row.children;
          row.isExpanded = false;
          updateState();
        };

        // toggle
        const ExpandCollapsToggle = ({row, expandRow, collapseRow}) => {
          // display +/- only if nextsplit is not undefined or null
          if (row.nextSplit) {
            if (row.isExpanded === true) {
              return (<button type="button" onClick={() => collapseRow(row)}>
                -
              </button>);
            } else {
              return (<button type="button" onClick={() => expandRow(row)}>
                +
              </button>);
            }
          } else {
            return null;
          }
        };



        if (rows) {
          return rows.map((row) => {
          //  if(!_.isEmpty(row)) {
              return (<Fragment key={row.id}>
                <tr key={row.id}>
                  <td>
                    <ExpandCollapsToggle row={row} expandRow={expandRow} collapseRow={collapseRow}/>{" "}
                    {row.split}
                    - {row.id}
                  </td>
                  <td>{row.name}</td>
                </tr>
                <ExpandableTableRow rows={row.children}/>
              </Fragment>);
          //  }

          });
        } else {
          return null;
        }
      };

      const splitData = this.state.splitOption.map((ob) => {
        return (<Fragment key={ob}><span>{ob}</span> > </Fragment>)
      })

      if (this.state.data) {
        return (
          <Fragment>
            {splitData} <br/>
            <button onClick = {()=>addNewSplit("name")}>camp name</button>
            <button onClick = {()=>addNewSplit("os")}>os</button>
            <button onClick = {()=>addNewSplit("country")}>country</button>
          <ExpandableTableRow rows={this.state.data} />
        </Fragment>
        );
      } else {
        return null;
      }
    }
  }

  export default TableRowData;

我也创建了codeandbox.io的示例-链接

Also i have create example of codesandbox.io - Link

这是您如何使用ui来复制场景

Here is how you play with ui to replicate scenario

  • 首先单击阵营名称,将显示展开图标
  • 如果需要,现在展开即可,您可以根据以下内容查看数据
  • 现在再添加一个拆分的操作系统或国家/地区,您将看到带有第二级行的展开图标
  • 下一步是删除营地名称",这是在删除营地名称时出现的问题,应根据可用拆分重新呈现表格,在这种情况下,应删除用户的所有行,并且+图标必须存在下一个可用的拆分操作系统或国家/地区,我使用了默认的拆分ID,它将始终存在

推荐答案

import React, { useState, useEffect, useRef, Fragment } from "react";
import axios from "axios";

const test_data = [{
  "id":1,
    "name":"Leanne Graham",
    "username":"Bret",
    "email":"Sincere@april.biz",
    "address":{
      "street":"Kulas Light",
      "suite":"Apt. 556",
      "city":"Gwenborough",
      "zipcode":"92998-3874",
      "geo":{
        "lat":"-37.3159",
        "lng":"81.1496"
      }
    },
    "phone":"1-770-736-8031 x56442",
    "website":"hildegard.org",
    "company":{
      "name":"Romaguera-Crona",
      "catchPhrase":"Multi-layered client-server neural-net",
      "bs":"harness real-time e-markets"
    }
}];

class TableRowData extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      showIcon: false,
      selection: [],
      data: [],
      splitOption: ["campid"]
    };
  }

  // function to check if next split is there or not if there than return nextsplit
  checkIfSplitExist = (currentSplit) => {
    const i = this.state.splitOption.indexOf(currentSplit);

    if (i > -1 && this.state.splitOption[i + 1]) {
      return this.state.splitOption[i + 1];
    }
    return null;
  }

  getReportData = () => {
    // axios.get("https://jsonplaceholder.typicode.com/users?_start=0&_limit=1").then(({data}) => {
      this.setState({
        data: test_data.map((row) => {
        row.name = this.state.splitOption[0];
        row.isExpanded = false;
        row.currentSplit = this.state.splitOption[0];
        row.nextSplit = this.checkIfSplitExist(this.state.splitOption[0])
        row.parentId = 0;
        row.isVisble = true;
        console.log(row)
        return row;
      })
      });
    // });
  }
  
  componentDidMount() {
    this.getReportData()
  }

  render() {
    // update state function
    const updateState = () => {
      this.setState({
        data: [...this.state.data],
        splitOption: [...this.state.splitOption],
        selection: [...this.state.selection],
      }, () => { })
    }

    const recursionUpdateAndDeleteRow = (parentRow, childRow, split, index = 0) => {
      childRow.children && childRow.children.forEach((r) => {
        recursionUpdateAndDeleteRow(childRow, r, split, index + 1);
      });

      if (parentRow && split.indexOf(childRow.currentSplit) == -1) {
        delete parentRow.children;
      }

      childRow.currentSplit = split[index];
      childRow.nextSplit = split[index + 1] || null;
      if (!childRow.children) {
        childRow.isExpanded = false;
      }
    }

    const recursionUpdateAndDeleteRows = (rows, split) => {
      const _copy = [...rows];
      _copy.forEach((row) => {
        recursionUpdateAndDeleteRow(null, row, split);
      });
      return _copy;
    }

    const toggleSplit = (split) => {
      const index = this.state.splitOption.indexOf(split);
      let currentSplitOptions = [...this.state.splitOption];
      if (index > -1) {
        currentSplitOptions.splice(index, 1)
      }
      else {
        currentSplitOptions.push(split);
      }

      const _data = recursionUpdateAndDeleteRows(this.state.data, currentSplitOptions);

      this.setState({
        splitOption: currentSplitOptions,
        data: _data
      })
    }

    // add lazyload expand data
    const ExpandableTableRow = ({ rows }) => {

      const expandRow = (row) => {
        row.children = [
          {
            id: "_" + Math.random().toString(36).substr(2, 5),
            name: row.id + "_" + row.nextSplit,
            isExpanded: false,
            parentId: row.id,
            currentSplit: row.nextSplit,
            nextSplit: this.checkIfSplitExist(row.nextSplit),
            isVisble: true
          }, {
            id: "_" + Math.random().toString(36).substr(2, 5),
            name: row.id + "_" + row.nextSplit,
            isExpanded: false,
            parentId: row.id,
            currentSplit: row.nextSplit,
            nextSplit: this.checkIfSplitExist(row.nextSplit),
            isVisble: true
          }
        ];
        row.isExpanded = true;
        updateState();
      };

      // call whenever - click
      const collapseRow = (row) => {
        delete row.children;
        row.isExpanded = false;
        updateState();
      };

      // toggle
      const ExpandCollapsToggle = ({ row }) => {
        // display +/- only if nextsplit is not undefined or null
        if (row.nextSplit) {
          if (row.isExpanded) {
            return (
              <button type="button" onClick={() => collapseRow(row)}>
                -
              </button>
            );
          }
          return (
            <button type="button" onClick={() => expandRow(row)}>
              +
            </button>
          );
        }
        return null;
      };

      if (rows) {
        return rows.map((row) => {
          return (
            <Fragment key={row.id}>
            <tr key={row.id}>
              <td>
                <ExpandCollapsToggle
                  row={row}
                />
                {" "}{row.split} - {row.id}
              </td>
              <td>{row.name}</td>
            </tr>
            <ExpandableTableRow rows={row.children} />
          </Fragment>
          );
        });
      } else {
        return null;
      }
    };

    if (this.state.data) {
      return (
        <Fragment>
          {this.state.splitOption.join(', ')} <br />
          <button onClick={() => toggleSplit("name")}>
            camp name
          </button>
          <button onClick={() => toggleSplit("os")}>os</button>
          <button onClick={() => toggleSplit("country")}>country</button>
          <br />
          <ExpandableTableRow rows={this.state.data} />
        </Fragment>
      );
    } else {
      return null;
    }
  }
}

export default function App() {
  return (
    <div>
      <TableRowData />
    </div>
  );
}

在这里工作示例

这篇关于如何从对象中递归删除子对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆