Remove object from an array of hierarchical object [duplicate] - javascript

This question already has answers here:
Recursively filter array of objects
(9 answers)
Closed 4 years ago.
I have an array that looks something like this:
var a = [
{
value: 'Some data',
structure: 'linear',
id: 0,
children: [
{ id: 1, value: 'Some data', structure: undefined },
{ id: 2,
value: 'Some data',
structure: 'linear',
children: [
{ id: 4, value: 'Some data', structure: undefined }
]
}
]
},
{
id: 5,
structure: undefined
value: 'Some data',
},
];
I am trying to remove all the objects which value of structure is undefined.
Every matching object with no children, or matches in children hierarchy, should not exist in output array.
I don't know at run time how many levels of objects will have a children array
The output should look something like this:
const output = [
{
value: 'Some data',
structure: 'linear',
id: 0,
children: [
{ id: 2, value: 'Some data', structure: 'linear' }
]
}
];

Map over the data recursively and then return the result after filtering the undefined values like
var a = [
{
value: 'Some data',
structure: 'linear',
id: 0,
children: [
{ id: 1, value: 'Some data', structure: undefined },
{
id: 2,
value: 'Some data',
structure: 'linear',
children: [
{ id: 4, value: 'Some data', structure: undefined }
]
}
]
},
{
id: 5,
structure: undefined,
value: 'Some data',
},
];
const getReducedArr = (data) => {
return data.map((obj) => {
if (obj.structure) {
const children = obj.children ? getReducedArr(obj.children) : [];
return {
...obj,
...(children.length > 0 ? { children } : undefined)
}
}
}).filter(data => data !== undefined)
}
const res = getReducedArr(a);
console.log(res)

Related

Why async.map returns multiple copies of array?

const async = require('async');
const arr = [
{ name: 'john', id: '1' },
{ name: 'Andrie', id: '2' }]
let collectArr = [];
let data = async.mapLimit(arr, 5, async function (input) {
collectArr.push({ name: input.name, id: input.id });
return collectArr;
})
data.then((result) =>{
console.log('success',result);
}).catch(e => console.log('err'));
So here i am providing array to async.mapLimit without callback and expecting promise here.
Expected Output :- [ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ] ,
Got Result :-
[ [ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ],
[ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ] ]
So my question is why it is creating multiple copies of array, how to deal with this?
You are needlessly returning a sub array, and the same array reference each iteration, when all you want is to return the new object.
let data = async.mapLimit(arr, 5, async function (input) {
return { name: input.name, id: input.id };
});
Not sure why you need this to be async

Filter array inside array

I have the array as below
test_list = [
{
id: 1,
test_name: 'Test 1',
members: [
{
user_id: 3
},
{
user_id: 4
}
],
},
{
id: 2,
test_name: 'Test 2',
members: [
{
user_id: 4
},
{
user_id: 5
},
],
},
{
id: 3,
test_name: 'Test 2',
members: [
{
user_id: 8
},
{
user_id: 10
},
],
}
]
I want to filter the test for specific user_id, example if user_id = 4 I would like to have this result
{
id: 1,
...
},
{
id: 2,
...
},
I have tried with this but it only return the member
test_list.filter(function(item) {
item.members.filter(function(member) {
if(member.user_id === 4) {
return item;
}
});
})
Would anyone please help me in this case?
Check if .some of the objects in the members array have the user_id you're looking for:
test_list = [{
id: 1,
test_name: 'Test 1',
members: [{
user_id: 3
},
{
user_id: 4
}
],
},
{
id: 2,
test_name: 'Test 2',
members: [{
user_id: 4
},
{
user_id: 5
},
],
},
{
id: 3,
test_name: 'Test 2',
members: [{
user_id: 8
}]
}
];
const filtered = test_list.filter(
({ members }) => members.some(
({ user_id }) => user_id === 4
)
);
console.log(filtered);
You could use .reduce() and .filter() method of array to achieve required result.
Please check below working code snippet:
const arr = [{"id":1,"test_name":"Test 1","members":[{"user_id":3},{"user_id":4}]},{"id":2,"test_name":"Test 2","members":[{"user_id":4},{"user_id":5}]},{"id":3,"test_name":"Test 2","members":[{"user_id":8}]}];
const data = arr.reduce((r,{ members,...rest }) => {
let rec = members.filter(o => o.user_id === 4)
if(rec.length){
rest.members = rec;
r.push(rest);
}
return r;
},[]);
console.log(data);
Hope this works.
var members = item.members;
var filterById =members.filter((item1)=>{
return (item1.user_id===4)
});
return filterById.length > 0;
});
console.log(test_List_by_id)```

Get specific key depth in object with key value

const item = {
id: 'item1',
children: [
{ id: 'item1-1',
children: [
{ id: 'item1-1-1' },
{ id: 'item1-1-2' },
{ id: 'item1-1-3' },
]
},
{ id: 'item1-2',
children: [
{ id: 'item1-2-1' }
]
}
]
}
Like this,
function getLevelOfId(){
...
}
getLevelOfId('item1') =====> return 1
getLevelOfId('item1-2') =====> return 2
getLevelOfId('item1-1-1') =====> return 3
getLevelOfId('item1-1-2') =====> return 3
How to get specific object's depth with JavaScript?
Not use of id string. like ('item1-2').split('-').length Because each object has randomic id. Is there a simple way?
You need to iterate all objects and if found, take one for each level for the recursion depth.
function getLevelOfId(object, id) {
var level;
if (object.id === id) return 1;
object.children && object.children.some(o => level = getLevelOfId(o, id));
return level && level + 1;
}
const item = { id: 'item1', children: [{ id: 'item1-1', children: [{ id: 'item1-1-1' }, { id: 'item1-1-2' }, { id: 'item1-1-3' }] }, { id: 'item1-2', children: [{ id: 'item1-2-1' }] }] };
console.log(getLevelOfId(item, 'item1')); // 1
console.log(getLevelOfId(item, 'item1-2')); // 2
console.log(getLevelOfId(item, 'item1-1-1')); // 3
console.log(getLevelOfId(item, 'item1-1-2')); // 3
console.log(getLevelOfId(item, 'foo')); // undefined
if the structure id & children is fixed, how about search the whole value like "item1-1-1" in the json string:
{"id":"item1","children":[{"id":"item1-1","children":[{"id":"item1-1-1"},{"id":"item1-1-2"},{"id":"item1-1-3"}]},{"id":"item1-2","children":[{"id":"item1-2-1"}]}]}
level = (number of "{") - (number of "}") // before the searched positon of the string

Concat array from Object from Array

I'm currently trying to retrieve a list of metadata stored as an array, inside an object, inside an array. Here's a better explanatory example:
[
{
name: 'test',
metadata: [
{
name: 'Author',
value: 'foo'
},
{
name: 'Creator',
value: 'foo'
}
]
},
{
name: 'otherTest',
metadata: [
{
name: 'Created',
value: 'foo'
},
{
name: 'Date',
value: 'foo'
}
]
},
{
name: 'finalTest'
}
]
Now, my objective is to retrieve a list of metadata (by their name) without redundancy. I think that .map() is the key to success but I can't find how to do it in a short way, actually my code is composed 2 for and 3 if, and I feel dirty to do that.
The expected input is: ['Author', 'Creator', 'Created', 'Date']
I'm developping in Typescript, if that can help for some function.
You can use reduce() and then map() to return array of names.
var data = [{"name":"test","metadata":[{"name":"Author","value":"foo"},{"name":"Creator","value":"foo"}]},{"name":"otherTest","metadata":[{"name":"Created","value":"foo"},{"name":"Date","value":"foo"}]},{"name":"finalTest"}]
var result = [...new Set(data.reduce(function(r, o) {
if (o.metadata) r = r.concat(o.metadata.map(e => e.name))
return r
}, []))];
console.log(result)
You could use Set for unique names.
var data = [{ name: 'test', metadata: [{ name: 'Author', value: 'foo' }, { name: 'Creator', value: 'foo' }] }, { name: 'otherTest', metadata: [{ name: 'Created', value: 'foo' }, { name: 'Date', value: 'foo' }] }, { name: 'finalTest' }],
names = new Set;
data.forEach(a => (a.metadata || []).forEach(m => names.add(m.name)));
console.log([...names]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var data = [{"name":"test","metadata":[{"name":"Author","value":"foo"},{"name":"Creator","value":"foo"}]},{"name":"otherTest","metadata":[{"name":"Created","value":"foo"},{"name":"Date","value":"foo"}]},{"name":"finalTest"}]
data
.filter(function(obj){return obj.metadata != undefined})
.map(function(obj){return obj.metadata})
.reduce(function(a,b){return a.concat(b)},[])
.map(function(obj){return obj.name})
A hand to hand Array.prototype.reduce() and Array.prototype.map() should do it as follows;
var arr = [
{
name: 'test',
metadata: [
{
name: 'Author',
value: 'foo'
},
{
name: 'Creator',
value: 'foo'
}
]
},
{
name: 'otherTest',
metadata: [
{
name: 'Created',
value: 'foo'
},
{
name: 'Date',
value: 'foo'
}
]
},
{
name: 'finalTest'
}
];
result = arr.reduce((p,c) => c.metadata ? p.concat(c.metadata.map(e => e.name))
: p, []);
console.log(result);

Remove item from nested array by indices (recursion)

I have an array of nested objects. These objects take one of two forms:
// type a
{
value: 'some value'
}
or
// type b
{
array: [
object of type a or b,
object of type a or b,
...
]
}
So, the base array can be nested infinitely. Given a series of indices (I've been calling it a 'tree'), how can I remove a single item at any depth?
A sample of what I have so far:
const baseArray = [
{ value: 'some value' },
{ array: [
{ value: 'some value' },
{ array: [
{ value: 'some value' },
{ value: 'some value' },
],
},
{ value: 'some value' },
{ array: [
{ value: 'some value' },
{ array: [
{ value: 'delete me' },
{ value: 'some value' },
]
},
],
},
],
}
]
const tree = [1, 3, 1, 0]
function deleteNested(tree, inputArray) {
const index = tree.shift();
console.log(inputArray, index);
const child = inputArray[index].array;
if (tree.length > 0) {
console.log(child)
return deleteNested(tree, child);
}
return [
...inputArray.slice(0, index),
...inputArray.slice(index + 1)
]
}
const originalArray = baseArray.slice(0);
console.log(deleteNested(tree, baseArray), originalArray);
I want to delete the marked object given it's 'tree' location: [1, 3, 1, 0]:
first, look in the 1 (index of 1, not 0) value of the initial array,
then the 3 value,
then look at the 1 value,
then finally remove the 0 value.
What I have above isn't working, but has gotten me started.
The function needs to be recursive to work at any depth. It should ideally not use splice() to avoid modifying the array passed into it -- rather, it should return a new one.
As i said in the comments if you know the number of iterations in advance you shouldn't use a recursive approach. A while loop is ideal such as;
function delNestedItem(a,dm){
var i = 0;
while (i < dm.length-1) a = a[dm[i++]].array;
a.splice(dm[i],1);
}
var data = [
{ value: 'some value' },
{ array: [
{ value: 'some value' },
{ array: [
{ value: 'some value' },
{ value: 'some value' },
],
},
{ value: 'some value' },
{ array: [
{ value: 'some value' },
{ array: [
{ value: 'delete me' },
{ value: 'some value' },
]
},
],
},
],
}
],
delMark = [1, 3, 1, 0];
delNestedItem(data,delMark);
console.log(JSON.stringify(data,null,2));
Here's how you can do it in 1 reduce:
const baseArray = [{
value: 'some value'
}, {
array: [{
value: 'some value'
}, {
array: [{
value: 'some value'
}, {
value: 'some value'
}, ],
}, {
value: 'some value'
}, {
array: [{
value: 'some value'
}, {
array: [{
value: 'delete me'
}, {
value: 'some value'
}, ]
}, ],
}, ],
}];
const tree = [1, 3, 1, 0];
var deleted = tree.reduce(function(pos, pathIndex, index, arr) {
if (index + 1 < arr.length) {
return pos.array
? pos.array[pathIndex]
: pos[pathIndex];
} else {
pos.array = pos.array
.slice(0, pathIndex)
.concat(pos.array.slice(pathIndex + 1));
return pos;
}
}, baseArray);
console.log(baseArray);
You could generate a new array out of the given array only with the undeleted parts.
function ff(array, tree) {
function iter(array, level) {
var r = [];
array.forEach(function (a, i) {
if (tree[level] !== i) {
return r.push(a);
}
if (level + 1 !== tree.length && a.array) {
r.push({ array: iter(a.array, level + 1) });
}
});
return r;
}
return iter(array, 0);
}
var baseArray = [{ value: 'some value' }, { array: [{ value: 'some value' }, { array: [{ value: 'some value' }, { value: 'some value' }, ], }, { value: 'some value' }, { array: [{ value: 'some value' }, { array: [{ value: 'delete me' }, { value: 'some value' }, ] }, ], }, ], }],
tree = [1, 3, 1, 0],
copy = ff(baseArray, tree);
console.log(copy);
console.log(baseArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources