How to remove children objects recursively from object?

Janak Prajapati

I am working on solution

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

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

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

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

Consider this scenario

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

this is Ui will look like

+ 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

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

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 +

Here is my code

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;

Also i have create example of codesandbox.io - Link

Here is how you play with ui to replicate scenario

  • First click on camp name, expand icon will appear
  • Now expand if you want to, you can see data according split under
  • Now add one more split OS or Country and you can see expand icon with 2nd level rows
  • Next step is to remove "Camp Name", Here is issue when camp name is removed, table should be re render according available splits, in our case user's all row should be removed and + icon must be there are we have next split os or country available, i used default split id, it will be there always
Chandan
import React, { useState, useEffect, useRef, Fragment } from "react";
import axios from "axios";

const test_data = [{
  "id":1,
    "name":"Leanne Graham",
    "username":"Bret",
    "email":"[email protected]",
    "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>
  );
}

Here working example

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

How to remove children objects recursively from object?

From Dev

Remove Children from Json object

From Dev

How do I get all the children objects of the children objects of an object?

From Dev

How to remove a specific Object from an array of Objects, by object's property?

From Dev

jq: easiest way to recursively remove objects based on object value condition

From Dev

jq: easiest way to recursively remove objects based on object value condition

From Dev

Remove object from array of objects

From Dev

How to remove child objects from a @ManyToMany relation with lots of children in JPA and Hibernate

From Dev

How to destroy specific ( not remove ) objects/paths/group-children from layer in Paper.js

From Dev

How to remove duplicates from a list of custom objects, by a property of the object

From Dev

How to remove an object from an array of objects in C#

From Dev

How to remove children from a UIView in Xamarin?

From Dev

How to recursively return the number of 'toy' items in the direct object and all of that object's direct children

From Dev

Hibernate doesn't remove object from collection with children in specific case

From Dev

How to remove objects from NSArrrayController

From Dev

Remove Duplicate Objects From NSMutableArray object property?

From Dev

php remove object from array of objects

From Dev

How to recursively remove execute permissions from files without touching folders?

From Dev

How to recursively remove all hyphen characters from filenames

From Dev

How to remove children in JavaScript

From Dev

How to remove children of QTreeWidgetItem

From Dev

How to remove children in JavaScript

From Dev

How can I extract data from a list of parent objects with children?

From Dev

how to remove a comma from an object?

From Dev

how to remove an object from json

From Dev

How to remove Children from an AbsoluteLayout in Xamarin.Forms?

From Dev

Referencing parent objects from children

From Dev

How to remove duplicated objects from NSArray?

From Dev

How to remove duples from list of objects?

Related Related

  1. 1

    How to remove children objects recursively from object?

  2. 2

    Remove Children from Json object

  3. 3

    How do I get all the children objects of the children objects of an object?

  4. 4

    How to remove a specific Object from an array of Objects, by object's property?

  5. 5

    jq: easiest way to recursively remove objects based on object value condition

  6. 6

    jq: easiest way to recursively remove objects based on object value condition

  7. 7

    Remove object from array of objects

  8. 8

    How to remove child objects from a @ManyToMany relation with lots of children in JPA and Hibernate

  9. 9

    How to destroy specific ( not remove ) objects/paths/group-children from layer in Paper.js

  10. 10

    How to remove duplicates from a list of custom objects, by a property of the object

  11. 11

    How to remove an object from an array of objects in C#

  12. 12

    How to remove children from a UIView in Xamarin?

  13. 13

    How to recursively return the number of 'toy' items in the direct object and all of that object's direct children

  14. 14

    Hibernate doesn't remove object from collection with children in specific case

  15. 15

    How to remove objects from NSArrrayController

  16. 16

    Remove Duplicate Objects From NSMutableArray object property?

  17. 17

    php remove object from array of objects

  18. 18

    How to recursively remove execute permissions from files without touching folders?

  19. 19

    How to recursively remove all hyphen characters from filenames

  20. 20

    How to remove children in JavaScript

  21. 21

    How to remove children of QTreeWidgetItem

  22. 22

    How to remove children in JavaScript

  23. 23

    How can I extract data from a list of parent objects with children?

  24. 24

    how to remove a comma from an object?

  25. 25

    how to remove an object from json

  26. 26

    How to remove Children from an AbsoluteLayout in Xamarin.Forms?

  27. 27

    Referencing parent objects from children

  28. 28

    How to remove duplicated objects from NSArray?

  29. 29

    How to remove duples from list of objects?

HotTag

Archive