如何从对象中递归删除子对象?
Posted
技术标签:
【中文标题】如何从对象中递归删除子对象?【英文标题】:How to remove children objects recursively from object? 【发布时间】:2021-05-29 12:31:19 【问题描述】:我正在研究解决方案
每当用户单击展开与单击的行相关的数据时,我已经创建了基本的树型表,该表将根据行数据出现在其下方
我已经实现了展开/折叠最多 N 个嵌套级别的基本功能。
但我只遇到一个问题,所以基本上所有行都有基于具有多个值的数组的条件展开按钮
假设它是具有 3 个条目县、市、州的拆分数组
默认加载的数据将从 api 获取,现在我必须检查数组是否有可用的拆分!如果是的话,我会让展开按钮可见
考虑这种情况
const split = ["country","city","state"]
这是 UI 的样子
+ Data_1
+ Data_2
单击按钮 + 新数据表行将根据下一个可用拆分呈现,在我们的例子中是国家/地区,因此视觉表示会像
- Data_1
Country_1
Country_2
+ Data_2
这里国家没有展开按钮,因为用户还没有添加下一个拆分,让我们添加城市,并假设用户点击了 Country_1,所以数据会像
- Data_1
- Country_1
City_1
City_2
+ Country_2
+ Data_2
我的解决方案工作正常,直到这个级别现在可以说用户已从拆分中删除国家/地区和城市的所有节点,并且 - data_1 的图标应更改为 +
这是我的代码
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;
我也创建了codesandbox.io的例子-Link
这是你如何使用 ui 来复制场景
首先点击阵营名称,会出现展开图标 现在如果你想扩展,你可以看到根据拆分的数据 现在再添加一个拆分操作系统或国家/地区,您可以看到带有 2 级行的展开图标 下一步是删除“阵营名称”,这是删除阵营名称时的问题,应根据可用的拆分重新渲染表格,在我们的例子中,用户的所有行应该被删除,+ 图标必须在那里我们有下一个拆分操作系统或国家/地区可用,我使用默认拆分 ID,它将始终存在【问题讨论】:
请检查答案。 【参考方案1】: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>
);
在这里工作example
【讨论】:
谢谢@chandan以上是关于如何从对象中递归删除子对象?的主要内容,如果未能解决你的问题,请参考以下文章