How do I remove object (element) in Javascript object? - javascript

How do I remove an element from a JavaScript object by ID?
For instance I have to remove 004 or 007:
const obj = {
id: '001',
children: [
{
id: '002',
children: [
{
id: '003',
children: [],
},
{
id: '004',
children: [
{
id: '005',
children: [],
}
],
}
],
},
{
id: '006',
children: [
{
id: '007',
children: [],
}
],
},
]
}
i am trying to like this, find id but what should be next. It is expected to remove id from the object.
const removeById = (obj = {}, id = '') => {
console.log('obj: ', obj)
const search = obj.children.find(o => o.id === id)
console.log('##search: ', search)
if(search) {
console.log('## parent id: ', obj.id)
...
}
if (obj.children && obj.children.length > 0) {
obj.children.forEach(el => removeById(el, id));
}
}
removeById(obj, '007')

You can use findIndex to get the location in the array.
To remove an element from an array you need to use splice.
You can then loop over the children with some and check the children's children. Using some, you can exit out when you find the id so you do not have to keep looping.
let obj = {
id: '001',
children: [
{
id: '002',
children: [
{
id: '003',
children: [],
},
{
id: '004',
children: [
{
id: '005',
children: [],
}
],
}
],
},
{
id: '006',
children: [
{
id: '007',
children: [],
}
],
},
]
}
const removeById = (parentData, removeId) => {
// This is only the parent level!
// If you will never delete the first level parent, this is not needed
if (parentData.id === removeId){
Object.keys(data).forEach(key => delete parentData[key]);
return true;
}
function recursiveFind (children) {
// check if any of the children have the id
const index = children.findIndex(({id}) => id === removeId);
// if we have an index
if (index != -1) {
// remove it
children.splice(index, 1);
// say we found it
return true;
}
// Loop over the chldren check their children
return children.some(child => recursiveFind(child.children));
}
return recursiveFind(parentData.children);
}
removeById(obj,'004');
removeById(obj,'007');
console.log(obj)

We can use a recursive function to do it
let obj = {
id: '001',
children: [
{
id: '002',
children: [
{
id: '003',
children: [],
},
{
id: '004',
children: [
{
id: '005',
children: [],
}
],
}
],
},
{
id: '006',
children: [
{
id: '007',
children: [],
}
],
},
]
}
const removeById = (data,id) =>{
if(data.id === id){
delete data.id
delete data.children
return
}
data.children.forEach(d =>{
removeById(d,id)
})
}
removeById(obj,'006')
console.log(obj)
Update: not leave an empty object after removing
let obj = {
id: '001',
children: [
{
id: '002',
children: [
{
id: '003',
children: [],
},
{
id: '004',
children: [
{
id: '005',
children: [],
}
],
}
],
},
{
id: '006',
children: [
{
id: '007',
children: [],
}
],
},
]
}
let match = false
const removeById = (data,id) =>{
match = data.some(d => d.id == id)
if(match){
data = data.filter(d => d.id !== id)
}else{
data.forEach(d =>{
d.children = removeById(d.children,id)
})
}
return data
}
let data = [obj]
console.log(removeById(data,'004'))

Related

How do I sort an object with children properties?

I have an object that represents a tree:
const obj = {
"1": {
id: "1",
children: ["1-1", "1-2"]
},
"1-1": {
id: "1-1",
children: ["1-1-1", "1-1-2"]
},
"1-2": {
id: "1-2",
children: []
},
"1-1-1": {
id: "1-1-1",
children: []
},
"1-1-2": {
id: "1-1-2",
children: []
}
};
The result is a list similar to:
<ul>
<li>
1
<ul>
<li>
1.1
<ul>
<li>1.1.1</li>
<li>1.1.2</li>
</ul>
</li>
<li>
1.2
</li>
</ul>
</li>
</ul>
What I need is to transform the object above to an array where items go in the order they do in the list representation, i.e. ['1', '1-1', '1-1-1', '1-1-2', '1-2']. Ids can be any so I can't rely on them. It's the order of items in the children property that matters.
Update
The final result should be ['1', '1-1', '1-1-1', '1-1-2', '1-2'] i.e. the order they come in the list from the top to the bottom.
I use DFS to parse. It can sort any depth data. (You can try the obj2)
const obj = {
"1": {
id: "1",
children: ["1-1", "1-2"]
},
"1-1": {
id: "1-1",
children: ["1-1-1", "1-1-2"]
},
"1-2": {
id: "1-2",
children: []
},
"1-1-1": {
id: "1-1-1",
children: []
},
"1-1-2": {
id: "1-1-2",
children: []
}
};
const obj2 = {
"2": {
id: "2",
children: ["2-1", "2-2", "2-3"]
},
"2-1": {
id: "2-1",
children: ["2-1-1", "2-1-2"]
},
"2-2": {
id: "2-2",
children: []
},
"2-3": {
id: "2-3",
children: []
},
"2-1-1": {
id: "2-1-1",
children: ["2-1-1-1", "2-1-1-2"]
},
"2-1-2": {
id: "2-1-2",
children: ["2-1-2-1"]
},
"2-1-1-1": {
id: "2-1-1-1",
children: []
},
"2-1-1-2": {
id: "2-1-1-2",
children: []
},
"2-1-2-1": {
id: "2-1-2-1",
children: []
},
};
/* DFS */
function sort(id) {
if (!sorted.includes(id)) {
sorted.push(id);
obj[id].children.forEach(sub => {
sort(sub);
});
}
}
/* MAIN */
let sorted = [];
for (let [id, value] of Object.entries(obj)) {
sort(id);
}
console.log(sorted.flat());
const obj={1:{id:"1",children:["1-1","1-2"]},"1-1":{id:"1-1",children:["1-1-1","1-1-2"]},"1-2":{id:"1-2",children:[]},"1-1-1":{id:"1-1-1",children:[]},"1-1-2":{id:"1-1-2",children:[]}};
const output = Object.keys(obj)
// remove every non root
Object.entries(obj).forEach(el => el[1].children.forEach(child => {
let index = output.indexOf(child)
if (index !== -1) {
output.splice(index, 1)
}
}))
for (let i = 0; i < output.length; i++) {
// for each get it's children
let children = obj[output[i]].children
// push them just behind it
output.splice(i + 1, 0, ...children)
}
console.log(output)
You could try a recursive call with the base condition to ignore the traversed node
const obj = {
"1": {
id: "1",
children: ["1-1", "1-2"],
},
"1-1": {
id: "1-1",
children: ["1-1-1", "1-1-2"],
},
"1-2": {
id: "1-2",
children: [],
},
"1-1-1": {
id: "1-1-1",
children: [],
},
"1-1-2": {
id: "1-1-2",
children: [],
},
}
function traverse(obj) {
const res = []
const traversed = {}
function getChildren(id) {
if (traversed[id]) {
return
}
res.push(id)
traversed[id] = true
obj[id].children.forEach((childId) => getChildren(childId))
}
for (const id in obj) {
getChildren(id)
}
return res
}
console.log(traverse(obj))
Hope this is what you are expecting ?
let ans = []
function recursiveCallObj(key){
!ans.includes(key) ? ans.push(key) : ""
for(let i=0; i< obj[key].children.length; i++){
if(!ans.includes(obj[key].children[i])){
recursiveCallObj(obj[key].children[i])
}
else{
return
}
}
}
for(let [key, value] of Object.entries(obj)){
if(value.children.length > 0){
recursiveCallObj(key)
}
else{
!ans.includes(key) ? ans.push(key) : ""
}
}
console.log(ans)

Nested array filtering, is there a more elegant way?

I'm trying to filter a nested structure, based on a search string.
If the search string is matched in an item, then I want to keep that item in the structure, along with its parents.
If the search string is not found, and the item has no children, it can be discounted.
I've got some code working which uses a recursive array filter to check the children of each item:
const data = {
id: '0.1',
children: [
{
children: [],
id: '1.1'
},
{
id: '1.2',
children: [
{
children: [],
id: '2.1'
},
{
id: '2.2',
children: [
{
id: '3.1',
children: []
},
{
id: '3.2',
children: []
},
{
id: '3.3',
children: []
}
]
},
{
children: [],
id: '2.3'
}
]
}
]
};
const searchString = '3.3';
const filterChildren = (item) => {
if (item.children.length) {
item.children = item.children.filter(filterChildren);
return item.children.length;
}
return item.id.includes(searchString);
};
data.children = data.children.filter(filterChildren);
console.log(data);
/*This outputs:
{
"id": "0.1",
"children": [
{
"id": "1.2",
"children": [
{
"id": "2.2",
"children": [
{
"id": "3.3",
"children": []
}
]
}
]
}
]
}*/
I'm concerned that if my data structure becomes massive, this won't be very efficient.
Can this be achieved in a 'nicer' way, that limits the amount of looping going on? I'm thinking probably using a reducer/transducer or something similarly exciting :)
A nonmutating version with a search for a child.
function find(array, id) {
var child,
result = array.find(o => o.id === id || (child = find(o.children, id)));
return child
? Object.assign({}, result, { children: [child] })
: result;
}
const
data = { id: '0.1', children: [{ children: [], id: '1.1' }, { id: '1.2', children: [{ children: [], id: '2.1' }, { id: '2.2', children: [{ id: '3.1', children: [] }, { id: '3.2', children: [] }, { id: '3.3', children: [] }] }, { children: [], id: '2.3' }] }] },
searchString = '3.3',
result = find([data], searchString);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Filter nested object from parent object

I have a parent object with a child array of objects nest underneath. Each object contains an id key with a unique value. A filter function needs to search the parent object for an id, if it does not equal the given id then recursively search through nested objects for the id until it is found. Once the object with the given key is found the remove and return the updated myObject.
The structure looks as followed:
let myObject = {
key: 1,
name: 'hello',
children: [
{
key: 2,
name: 'world',
children: []
},
{
key: 3,
name: 'hope',
children: [
{
key: 4,
name: 'you',
children: [{
key: 5,
name: 'are',
children: []
}]
},
{
key: 6,
name: 'having',
children: [{
key: 7,
name: 'fun',
children: []
}]
}
]
}
]
}
let given = 4;
if (myObject.key !== given) {
myObject = searchChild(myObject, given)
} else {
myObject = {}
}
function searchChild(parent, given) {
parent.children.map(child => {
return child.children.filter(item => {
if (item.key === given) return item;
else if (item.key !== given
&& child.children.length > 0
&& child.children != undefined) {
searchChild(child.children, given);
}
})
})
}
Currently, I am receiving a type error when running the recursive function.
The output should look like where the keys are updated to the new order in tree:
{
key: 1,
name: 'hello',
children: [
{
key: 2,
name: 'world',
children: []
},
{
key: 3,
name: 'hope',
children: [
{
key: 4,
name: 'having',
children: [{
key: 5,
name: 'fun',
children: []
}]
}
]
}
]
}
Here is function you can call for your object
function searchInChild(parent,key){
parent.children = parent.children.filter((c)=>{
if(key == c.key ){
result = c;
return false;
}
return true;
});
if(result == null){
for(c in parent.children){
searchInChild(parent.children[c],key);
}
}
}
Where, you can simply pass searchInChild(myObject,key) & make result global variable.
You pass child.children but you have to pass child you already iterate through children in the function.
let myObject = {
key: 1,
name: 'hello',
children: [
{
key: 2,
name: 'world',
children: []
},
{
key: 3,
name: 'hope',
children: [
{
key: 4,
name: 'you',
children: [{
key: 5,
name: 'are',
children: []
}]
},
{
key: 6,
name: 'having',
children: [{
key: 7,
name: 'fun',
children: []
}]
}
]
}
]
}
let given = 4;
if (myObject.key !== given) {
myObject = searchChild(myObject, given)
} else {
myObject = {}
}
function searchChild(parent, given) {
if(parent && parent.children) {
parent.children.map(child => {
return child.children.filter(item => {
if (item.key === given) return item;
else if (item.key !== given
&& child.children.length > 0
&& child.children != undefined) {
searchChild(child, given);
}
})
})
}
}

Search for an object in a recursive object structure (representing a file system)

I'd like to pass as an input the name of a folder that I want to search for, and get as an output the object that it belongs to.
My array is like this:
const array = {
item: [{
name: "parentFolder1",
item: [{
name: "subFolder1",
item: []
},
{
name: "subFolder2",
item: []
}
]
},
{
name: "parentFolder2",
item: [{
name: "sub1",
item: []
},
{
name: "sub2",
item: []
}
]
}
]
};
const sub = Object.values(array).map(x =>
x.find(y => y.item.find(obj => obj.name = "sub2")))
console.dir(sub)
The output I want:
{
name: "sub2",
item: []
}
The output I get:
[ { name: 'parentFolder1', item: [ [Object], [Object] ] } ]
const array = {
item: [{
name: "parentFolder1",
item: [{
name: "subFolder1",
item: []
},
{
name: "subFolder2",
item: []
}
]
},
{
name: "parentFolder2",
item: [{
name: "sub1",
item: []
},
{
name: "sub2",
item: []
}
]
}
]
};
const val = array.item.map(folder => folder.item.find(obj => obj.name === "sub2")).find(val => val)
console.log(val)
If you need it to be recursive
function find(name, obj) {
if(obj.name === name) return obj;
return obj.item.reduce((result, item) => result || find(name, item), false)
}
const array = {
item: [{
name: "parentFolder1",
item: [{
name: "subFolder1",
item: []
},
{
name: "subFolder2",
item: [{
name: "subsub",
item: [{
name: "searchme",
item: []
}]
}]
}
]
},
{
name: "parentFolder2",
item: [{
name: "sub1",
item: []
},
{
name: "sub2",
item: []
}
]
}
]
};
console.log(find("searchme", array))

How to find a object in a nested array using recursion in JS

Consider the following deeply nested array:
const array = [
{
id: 1,
name: "bla",
children: [
{
id: 23,
name: "bla",
children: [{ id: 88, name: "bla" }, { id: 99, name: "bla" }]
},
{ id: 43, name: "bla" },
{
id: 45,
name: "bla",
children: [{ id: 43, name: "bla" }, { id: 46, name: "bla" }]
}
]
},
{
id: 12,
name: "bla",
children: [
{
id: 232,
name: "bla",
children: [{ id: 848, name: "bla" }, { id: 959, name: "bla" }]
},
{ id: 433, name: "bla" },
{
id: 445,
name: "bla",
children: [
{ id: 443, name: "bla" },
{
id: 456,
name: "bla",
children: [
{
id: 97,
name: "bla"
},
{
id: 56,
name: "bla"
}
]
}
]
}
]
},
{
id: 15,
name: "bla",
children: [
{
id: 263,
name: "bla",
children: [{ id: 868, name: "bla" }, { id: 979, name: "bla" }]
},
{ id: 483, name: "bla" },
{
id: 445,
name: "bla",
children: [{ id: 423, name: "bla" }, { id: 436, name: "bla" }]
}
]
}
];
How would I grab a certain object by key that might be deeply nested, using recursion?
I have tried this, but this won't work for nesting deeper than 2 levels, it then just returns undefined:
const findItemNested = (arr, itemId, nestingKey) => {
for (const i of arr) {
console.log(i.id);
if (i.id === itemId) {
return i;
}
if (i[nestingKey]) {
findItemNested(i[nestingKey], itemId, nestingKey);
}
}
};
The result should be:
const res = findItemNested(array, 959, "children"); >> { id: 959, name: "bla" }
This can perhaps also be achieved using .find, or just to flatten the array (by the children key), but using recursion seems like the most logical solution to me. Does anybody have a solution to this?
Thanks in advance :).
You might use a recursive reduce:
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
const findItemNested = (arr, itemId, nestingKey) => (
arr.reduce((a, item) => {
if (a) return a;
if (item.id === itemId) return item;
if (item[nestingKey]) return findItemNested(item[nestingKey], itemId, nestingKey)
}, null)
);
const res = findItemNested(array, 959, "children");
console.log(res);
This should work:
function findByIdRecursive(array, id) {
for (let index = 0; index < array.length; index++) {
const element = array[index];
if (element.id === id) {
return element;
} else {
if (element.children) {
const found = findByIdRecursive(element.children, id);
if (found) {
return found;
}
}
}
}
}
You might also use recursion with Array.find like below
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
function findById(arr, id, nestingKey) {
// if empty array then return
if(arr.length == 0) return
// return element if found else collect all children(or other nestedKey) array and run this function
return arr.find(d => d.id == id)
|| findById(arr.flatMap(d => d[nestingKey] || []), id)
|| 'Not found'
}
console.log(findById(array, 12, 'children'))
console.log(findById(array, 483, 'children'))
console.log(findById(array, 1200, 'children'))
We use object-scan for most of our data processing. It's awesome for all sorts of things, but does take a while to wrap your head around. This is how one could answer your question:
// const objectScan = require('object-scan');
const find = (data, id) => objectScan(['**(^children$).id'], {
abort: true,
rtn: 'parent',
useArraySelector: false,
filterFn: ({ value }) => value === id
})(data);
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
console.log(find(array, 12));
// => { id: 12, name: 'bla', children: [ { id: 232, name: 'bla', children: [ { id: 848, name: 'bla' }, { id: 959, name: 'bla' } ] }, { id: 433, name: 'bla' }, { id: 445, name: 'bla', children: [ { id: 443, name: 'bla' }, { id: 456, name: 'bla', children: [ { id: 97, name: 'bla' }, { id: 56, name: 'bla' } ] } ] } ] }
console.log(find(array, 483));
// => { id: 483, name: 'bla' }
console.log(find(array, 959));
// => { id: 959, name: 'bla' }
console.log(find(array, 1200));
// => undefined
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan
You can do:
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
const findItemNested = (arr, itemId, nestingKey) => arr.reduce((a, c) => {
return a.length
? a
: c.id === itemId
? a.concat(c)
: c[nestingKey]
? a.concat(findItemNested(c[nestingKey], itemId, nestingKey))
: a
}, []);
const res = findItemNested(array, 959, "children");
if (res.length) {
console.log(res[0]);
}
This will use recursive find by level, it'll try to find the item in array and then call itself with the children of each item in the array:
New browsers will have Array.prototype.flatten but in this case I've added the flatten function separately.
const array = [{"id":1,"name":"bla","children":[{"id":23,"name":"bla","children":[{"id":88,"name":"bla"},{"id":99,"name":"bla"}]},{"id":43,"name":"bla"},{"id":45,"name":"bla","children":[{"id":43,"name":"bla"},{"id":46,"name":"bla"}]}]},{"id":12,"name":"bla","children":[{"id":232,"name":"bla","children":[{"id":848,"name":"bla"},{"id":959,"name":"bla"}]},{"id":433,"name":"bla"},{"id":445,"name":"bla","children":[{"id":443,"name":"bla"},{"id":456,"name":"bla","children":[{"id":97,"name":"bla"},{"id":56,"name":"bla"}]}]}]},{"id":15,"name":"bla","children":[{"id":263,"name":"bla","children":[{"id":868,"name":"bla"},{"id":979,"name":"bla"}]},{"id":483,"name":"bla"},{"id":445,"name":"bla","children":[{"id":423,"name":"bla"},{"id":436,"name":"bla"}]}]}];
const flatten = (arr) =>
arr.reduce((result, item) => result.concat(item), []);
const findBy = (findFunction, subItemsKey) => (array) =>
//array is empty (can be when children of children of children does not exist)
array.length === 0
? undefined //return undefined when array is empty
: array.find(findFunction) || //return item if found
findBy(findFunction, subItemsKey)(//call itself when item is not found
flatten(
//take children from each item and flatten it
//([[child],[child,child]])=>[child,child,child]
array.map((item) => item[subItemsKey] || []),
),
);
const findChildrenById = (array) => (value) =>
findBy((item) => item.id === value, 'children')(array);
const findInArray = findChildrenById(array);
console.log('found', findInArray(99));
console.log('not found', findInArray({}));
You need to iterate through your objects and then need to be parse each object using recursion. Try the answer mentioned here: JavaScript recursive search in JSON object
code:
`function findNode(id, currentNode) {
var i,
currentChild,
result;
if (id == currentNode.id) {
return currentNode;
} else {
// Use a for loop instead of forEach to avoid nested functions
// Otherwise "return" will not work properly
for (i = 0; i < currentNode.children.length; i += 1) {
currentChild = currentNode.children[i];
// Search in the current child
result = findNode(id, currentChild);
// Return the result if the node has been found
if (result !== false) {
return result;
}
}
// The node has not been found and we have no more options
return false;
}
}`

Categories

Resources