remove nested null values from javascript array - javascript

My javascript array is as below. I want to remove all the null values inside all the children arrays. I managed to remove as below. but i'm looking for more elegant solution other than this
let data = [
{
"id": "359816ba-4bc6-4b7f-b57c-d80331eee0a6",
"name": "organization 1",
"type": "org",
"title": "organization 1",
"children": [
null,
{
"id": "6571cada-490c-41db-97e8-197a9c0faabb",
"name": "location 3",
"org_id": "359816ba-4bc6-4b7f-b57c-d80331eee0a6",
"type": "location",
"title": "location 3",
"children": [
null,
{
"id": "8620fce9-f7d0-442a-86e8-f58e9029a164",
"name": "zone 3",
"zone_settings_id": null,
"location_id": "6571cada-490c-41db-97e8-197a9c0faabb",
"type": "zone",
"title": "zone 3",
"children": [
null,
null,
null
]
},
null,
null
]
},
{
"id": "93b8ad9e-59ee-4de5-ac32-d3d5d19b083c",
"name": "location 4",
"org_id": "359816ba-4bc6-4b7f-b57c-d80331eee0a6",
"type": "location",
"title": "location 4",
"children": [
null,
null,
{
"id": "db14daf4-4488-47fa-8d18-2d213b3a54a5",
"name": "zone 4",
"zone_settings_id": null,
"location_id": "93b8ad9e-59ee-4de5-ac32-d3d5d19b083c",
"type": "zone",
"title": "zone 4",
"children": [
null,
null,
{
"id": "6ae5b04a-1101-4d73-80e4-05d4db454406",
"gwId": "E4956E45107R",
"zone_id": "db14daf4-4488-47fa-8d18-2d213b3a54a5",
"org_id": "359816ba-4bc6-4b7f-b57c-d80331eee0a6",
"title": "E4:95:6E:45:10:7R"
}
]
},
{
"id": "c01398c6-7650-426b-936d-6b88b1b507f2",
"name": "zone 5",
"zone_settings_id": null,
"location_id": "93b8ad9e-59ee-4de5-ac32-d3d5d19b083c",
"type": "zone",
"title": "zone 5",
"children": [
null,
null,
null
]
}
]
},
null
]
},
{
"id": "46665d49-020d-411f-9f11-c9ddad9a741c",
"name": "organization 2",
"type": "org",
"title": "organization 2",
"children": [
null,
null,
null,
null
]
},
{
"id": "95e7d05b-fe67-422d-8617-9f10633ea6f6",
"name": "org 3",
"type": "org",
"title": "org 3",
"children": [
null,
null,
null,
{
"id": "0a8509d8-1fd8-486c-8457-3ff393c09abc",
"name": "location 1 of org 3",
"org_id": "95e7d05b-fe67-422d-8617-9f10633ea6f6",
"type": "location",
"title": "location 1 of org 3",
"children": [
null,
null,
null,
null
]
}
]
}
]
let orgs = data.filter(org => org != null);
orgs.forEach(org => {
org.children = org.children.filter(location => location != null);
})
orgs.forEach(org => {
org.children.forEach(loc => {
loc.children = loc.children.filter(zone => zone != null);
})
})
orgs.forEach(org => {
org.children.forEach(loc => {
loc.children.forEach(zone => {
zone.children = zone.children.filter(router => router != null);
})
})
})
console.log(orgs);

const noNull = array => array
.filter(it => it !== null)
.map(it => it.children ? { ...it, children: noNull(it.children) } : it);
const result = noNull(data);
You could recursively filter the arrays in an immutable way.
Or the mutating version would be:
const noNull = array => {
const result = array.filter(it => it !== null);
for(const value of result)
if(value.children) value.children = noNull(value.children);
return result;
};

Related

Build flat array with levels from tree array in javascript

I have the following array tree in javascript:
[
{
"id": 1,
"parentId": null,
"description": "Item 1",
"value": 0,
"children": [
{
"id": 2,
"parentId": 1,
"description": "Item 1.1",
"value": 0,
"children": [
{
"id": 3,
"parentId": 2,
"description": "Item 1.1.1",
"value": 0,
"children": []
}
]
}
]
},
{
"id": 4,
"parentId": null,
"description": "Item 2",
"value": 0,
"children": [
{
"id":5,
"parentId": 4,
"description": "Item 2.1",
"value": 0,
"children": []
}
]
}
]
I want to turn it into a flat one with it's levels, like this (see level attribute):
[
{
"id":1,
"parentId": null,
"description":"Item 1",
"value":0,
"level": "1"
},
{
"id":2,
"parentId": 1,
"description":"Item 1.1",
"value":0,
"level": "1.1"
},
{
"id":3,
"parentId": 2,
"description":"Item 1.1.1",
"value":0,
"level": "1.1.1"
},
{
"id":4,
"parentId": null,
"description":"Item 2",
"value":0,
"level": "2"
},
{
"id":5,
"parentId": 4,
"description":"Item 2.1",
"value":0,
"level": "2.1"
}
]
What's the best way to do this regardless of depth?
PS: I have the flat one too, but without "level" attribute and the proposal is to add this attribute based on parentId and sort list by it, like following:
Item 1
Item 1.1
Item 1.1.1
Item 2
Item 2.1
If you don't want to limit the solution by the depth of the array, then I suggest not to use recursion.
const solution = data => {
const stack = data.map((item, index) => ({ ...item, level: `${index + 1}` }))
const result = []
while (stack.length) {
const item = stack.pop()
const { children, ...restItem } = item
stack.push(...item.children.map((child, index) => ({ ...child, level: `${item.level}.${index + 1}` })))
result.push(restItem)
}
return result
}
const data = [
{
"id": 1,
"parentId": null,
"description": "Item 1",
"value": 0,
"children": [
{
"id": 2,
"parentId": 1,
"description": "Item 1.1",
"value": 0,
"children": [
{
"id": 3,
"parentId": 2,
"description": "Item 1.1.1",
"value": 0,
"children": []
}
]
}
]
},
{
"id": 4,
"parentId": null,
"description": "Item 2",
"value": 0,
"children": [
{
"id":5,
"parentId": 4,
"description": "Item 2.1",
"value": 0,
"children": []
}
]
}
]
console.log(solution(data))
If you recursively loop through your array (assuming that children will always be the key), something like this will work.
const arr = [
{
"id": 1,
"parentId": null,
"description": "Item 1",
"value": 0,
"children": [
{
"id": 2,
"parentId": 1,
"description": "Item 1.1",
"value": 0,
"children": [
{
"id": 3,
"parentId": 2,
"description": "Item 1.1.1",
"value": 0,
"children": []
}
]
}
]
},
{
"id": 4,
"parentId": null,
"description": "Item 2",
"value": 0,
"children": [
{
"id":5,
"parentId": 4,
"description": "Item 2.1",
"value": 0,
"children": []
}
]
}
]
const newArray = [];
const flatten = (item, parentIdx) => {
// separate parent from children
item.forEach(({ children, ...child}, idx) => {
// create level
const level = `${parentIdx ? `${parentIdx}.` : ''}${idx + 1}`;
// add parent to new array
newArray.push({...child, level});
// recursively flatten children
flatten(children, level);
})
}
flatten(arr)
console.log(newArray)

How to find the farthest object in a sub array of a array with empty objects?

I've got a array of object in which the children got arrays as well:
[{
"id": 0,
"name": "Primary",
"xPoints": [{
"id": 0,
"name": "Untitledtest12",
"type": "custom",
"description": ""
}, {
"id": 1,
"name": "asd",
"description": "",
"type": "custom"
}, {
"id": 2,
"name": "asd",
"description": "",
"type": "custom"
}, {
"id": 3,
"name": "asd123",
"description": "",
"type": "custom"
}, {
"id": 4,
"name": "a",
"description": "a",
"type": "custom"
}]
}, {
"id": 1,
"name": "Untitled X Line",
"xPoints": [
{},
{},
{},
{},
{},
{
"id": 0,
"name": "this is the farthest",
"type": "custom",
"description": ""
},
{}
]
}]
what I'm looking for is a way to get the "farthest" of object index on these childrens of the main array
Expected result:
index: 6 from second's "Untitled X Line"
You can use recursion to get the depth of the object.
const obj = [{
"id": 0,
"name": "Primary",
"xPoints": [{
"id": 0,
"name": "Untitledtest12",
"type": "custom",
"description": ""
}, {
"id": 1,
"name": "asd",
"description": "",
"type": "custom"
}, {
"id": 2,
"name": "asd",
"description": "",
"type": "custom"
}, {
"id": 3,
"name": "asd123",
"description": "",
"type": "custom"
}, {
"id": 4,
"name": "a",
"description": "a",
"type": "custom"
}]
}, {
"id": 1,
"name": "Untitled X Line",
"xPoints": [
{},
{},
{},
{},
{},
{
"id": 0,
"name": "this is the farthest",
"type": "custom",
"description": ""
},
{}
]
}]
function depthOf(object) {
var level = 1;
for(var key in object) {
if (!object.hasOwnProperty(key)) continue;
if(typeof object[key] == 'object'){
var depth = depthOf(object[key]) + 1;
level = Math.max(depth, level);
}
}
return level;
}
var maxLevels = 0;
var maxObj;
obj.forEach(e => maxLevels < depthOf(e) ? maxObj = e : '');
console.log(maxObj);

JavaScript array filtering embedded object array

how to filter array embedded object array?
I use filter and some, but the result is not my though.
var data = [
{
"app": "mail",
"scenarios": [
{
"name": "",
"description": "plugin 1",
"contacts": [
{
"resourceId": "001",
"isPrimary": false
}
]
},
{
"name": "app2",
"description": "plugin 2",
"contacts": [
{
"resourceId": "002",
"isPrimary": false
}
]
}
]
},
{
"app": "mail2",
"scenarios": [
{
"name": "app1",
"description": "plugin 1",
"contacts": [
{
"resourceId": "001",
"isPrimary": false
}
]
},
{
"name": "app2",
"description": "plugin 2",
"contacts": [
{
"resourceId": "002",
"isPrimary": false
}
]
}
]
}
];
result = data.filter(app => app.scenarios.some(scenario => scenario.contacts.some(concact => concact.resourceId == '001')));
I want to filter the data to
[
{
"app": "mail",
"scenarios": [
{
"name": "",
"description": "plugin 1",
"contacts": [
{
"resourceId": "001",
"isPrimary": false
}
]
}
]
},
{
"app": "mail2",
"scenarios": [
{
"name": "app1",
"description": "plugin 1",
"contacts": [
{
"resourceId": "001",
"isPrimary": false
}
]
}
]
}
]
var data = [{"app":"mail","scenarios":[{"name":"","description":"plugin 1","contacts":[{"resourceId":"001","isPrimary":false}]},{"name":"app2","description":"plugin 2","contacts":[{"resourceId":"002","isPrimary":false}]}]},{"app":"mail2","scenarios":[{"name":"app1","description":"plugin 1","contacts":[{"resourceId":"001","isPrimary":false}]},{"name":"app2","description":"plugin 2","contacts":[{"resourceId":"002","isPrimary":false}]}]}];
result = data.filter(app => app.scenarios.some(scenario => scenario.contacts.some(concact => concact.resourceId == '001')));
console.log(result);
I think you need to filter the scenarios array and contacts array both. If you want to get a scenario which has at least one contact with resourceId === "001" you could do the following.
let data = [
{
"app": "mail",
"scenarios": [
{
"name": "",
"description": "plugin 1",
"contacts": [
{
"resourceId": "001",
"isPrimary": false
}
]
},
{
"name": "app2",
"description": "plugin 2",
"contacts": [
{
"resourceId": "002",
"isPrimary": false
}
]
}
]
},
{
"app": "mail2",
"scenarios": [
{
"name": "app1",
"description": "plugin 1",
"contacts": [
{
"resourceId": "001",
"isPrimary": false
}
]
},
{
"name": "app2",
"description": "plugin 2",
"contacts": [
{
"resourceId": "002",
"isPrimary": false
}
]
}
]
}
];
for(let item of data) {
item.scenarios = item.scenarios.filter(value => {
let validContacts = value.contacts.filter(contact => {
return contact.resourceId === "001";
})
return validContacts.length > 0;
})
}
console.log(data);
var data = [
{
"app": "mail",
"scenarios": [
{
"name": "",
"description": "plugin 1",
"contacts": [
{
"resourceId": "001",
"isPrimary": false
}
]
},
{
"name": "app2",
"description": "plugin 2",
"contacts": [
{
"resourceId": "002",
"isPrimary": false
}
]
}
]
},
{
"app": "mail2",
"scenarios": [
{
"name": "app1",
"description": "plugin 1",
"contacts": [
{
"resourceId": "001",
"isPrimary": false
}
]
},
{
"name": "app2",
"description": "plugin 2",
"contacts": [
{
"resourceId": "002",
"isPrimary": false
}
]
}
]
}
];
data.forEach(dat => {
dat.scenarios = dat.scenarios.find(da => da.contacts.find(x=>x.resourceId === "001"));
});
console.log(data)
You can go thru the code see if it fits what you're looking. An assumption made which contacts array will only have one element. Let me know if it's otherwise

Search nested object and return whole path

I have below JavaScript with n level children and want to search for id and if any of item from has matching id than need to return object from root to matching item.
I want to return entire hierarchy of found item from root till object with it's children.
I tried with lodash and underscore and could not find easy solution.
input: {
"children": [{
"name": "Home",
"title": "Home",
"id": "home1",
"children": []
},
{
"name": "BUSINESS AND ROLE SPECIFIC",
"title": "BUSINESS AND ROLE SPECIFIC",
"id": "BAR1",
"children": [{
"name": "Global Businesses",
"title": "Global Businesses",
"id": "GB1",
"children": [{
"name": "Commercial Banking",
"title": "Commercial Banking",
"id": "CB1",
"children": [{
"name": "FLAGSHIP PROGRAMMES",
"title": "FLAGSHIP PROGRAMMES",
"id": "FG1",
"children": []
}]
}]
}]
},
{
"name": "RISK MANAGEMENT",
"title": "RISK MANAGEMENT",
"id": "RM1",
"children": []
}
]
}
Search: {
id: 'FG1'
}
return :{
"name": "BUSINESS AND ROLE SPECIFIC",
"title": "BUSINESS AND ROLE SPECIFIC",
"id": "BAR1",
"children": [{
"name": "Global Businesses",
"title": "Global Businesses",
"id": "GB1",
"children": [{
"name": "Commercial Banking",
"title": "Commercial Banking",
"id": "CB1",
"children": [{
"name": "FLAGSHIP PROGRAMMES",
"title": "FLAGSHIP PROGRAMMES",
"id": "FG1",
"children": [{}]
}]
}]
}]
}
You could use this function:
function findChild(obj, condition) {
if (Object.entries(condition).every( ([k,v]) => obj[k] === v )) {
return obj;
}
for (const child of obj.children || []) {
const found = findChild(child, condition);
// If found, then add this node to the ancestors of the result
if (found) return Object.assign({}, obj, { children: [found] });
}
}
// Sample data
var input = { "children": [{ "name": "Home", "title": "Home", "id": "home1", "children": [] }, { "name": "BUSINESS AND ROLE SPECIFIC", "title": "BUSINESS AND ROLE SPECIFIC", "id": "BAR1", "children": [{ "name": "Global Businesses", "title": "Global Businesses", "id": "GB1", "children": [{ "name": "Commercial Banking", "title": "Commercial Banking", "id": "CB1", "children": [{ "name": "FLAGSHIP PROGRAMMES", "title": "FLAGSHIP PROGRAMMES", "id": "FG1", "children": [] }] }] }] }, { "name": "RISK MANAGEMENT", "title": "RISK MANAGEMENT", "id": "RM1", "children": [] } ]},
search = { id: 'FG1' };
console.log(findChild(input, search));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use this also for searching with multiple conditions, which must be true at the same time:
search = { "name": "Global Businesses", "title": "Global Businesses" };
... would give you the object that has the specified name and title.
Follow-up question
You asked in comments:
Is there way to supply number to not remove children for given node in input. like,
const donotRemoveChildNode = 2;
console.log(findChild(input, search, donotRemoveChildNode ));
...so it will not remove that specific node's children if it matches condition?
Here, if we search for { id: 'FG1'} and supply donotRemoveChildNode = 2, it would not remove the first level children for "Commercial banking".
I would say the donotRemoveChildNode would have to be 3, as there are three levels of children arrays in the ancestor-hierarchy of the "Commercial banking" node. A value of 0 would show the first level children of the top-most children property.
Here is how that extra argument would work -- I added some records to the data to illustrate the difference in the output:
function findChild(obj, condition, removeChildNodesBefore = Infinity) {
if (Object.entries(condition).every( ([k,v]) => obj[k] === v )) {
return obj;
}
for (const child of obj.children || []) {
let found = findChild(child, condition, removeChildNodesBefore - 1);
if (found) {
return Object.assign({}, obj, {
children: removeChildNodesBefore <= 0
? obj.children.map( sibling =>
sibling == child ? found
: Object.assign({}, sibling, {children: []})
)
: [found]
});
}
}
}
var input = { "children": [{ "name": "Home", "title": "Home", "id": "home1", "children": [] }, { "name": "BUSINESS AND ROLE SPECIFIC", "title": "BUSINESS AND ROLE SPECIFIC", "id": "BAR1", "children": [{ "name": "Global Businesses", "title": "Global Businesses", "id": "GB1", "children": [{ "name": "test", "title": "test", "id": "xxx", "children": [{ "name": "testDeep", "title": "test", "id": "deep", "children": []}]}, { "name": "Commercial Banking", "title": "Commercial Banking", "id": "CB1", "children": [{ "name": "test", "title": "test", "id": "yyy", "children": []}, { "name": "FLAGSHIP PROGRAMMES", "title": "FLAGSHIP PROGRAMMES", "id": "FG1", "children": [] }] }] }] }, { "name": "RISK MANAGEMENT", "title": "RISK MANAGEMENT", "id": "RM1", "children": [] } ]},
search = { id: 'FG1' }
console.log(findChild(input, search, 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }
function getBranch(branches, leaf_id)
{
var result_branch = null;
branches.some(function(branch, idx) {
if (branch.id == leaf_id) {
result_branch = Object.assign({}, branch);
result_branch.children.forEach(function(child, idx) {
delete result_branch.children[idx].children;
});
return true;
} else {
let target_branch = getBranch(branch.children, leaf_id);
if (target_branch) {
result_branch = Object.assign({}, branch);
delete result_branch.children
result_branch.children = [target_branch];
return true;
}
}
return false;
});
return result_branch;
}
console.log(getBranch(input.children, 'GB1'));
One way is to first loop the root children, and then create another function to see if the Id exists in any of it's children.
var data = {
"children": [{
"name": "Home",
"title": "Home",
"id": "home1",
"children": []
},
{
"name": "BUSINESS AND ROLE SPECIFIC",
"title": "BUSINESS AND ROLE SPECIFIC",
"id": "BAR1",
"children": [{
"name": "Global Businesses",
"title": "Global Businesses",
"id": "GB1",
"children": [{
"name": "Commercial Banking",
"title": "Commercial Banking",
"id": "CB1",
"children": [{
"name": "FLAGSHIP PROGRAMMES",
"title": "FLAGSHIP PROGRAMMES",
"id": "FG1",
"children": []
}]
}]
}]
},
{
"name": "RISK MANAGEMENT",
"title": "RISK MANAGEMENT",
"id": "RM1",
"children": []
}
]
};
function hasId( id, data ) {
if (data.id === id) return true;
if (data.children) {
for (const child of data.children) {
if (hasId( id, child)) return true;
}
}
return false;
}
function search( id, data ) {
for (const child of data.children) {
if (hasId(id, child)) return child;
}
return null;
}
console.log(search( "FG1", data ));

Javascript sum of items in a complex object

I have some data that I'm counting and putting the totals into an array.
Here is the data and code:
var data = {
"cars": [
{
"id": "1",
"name": "name 1",
"thsub": [
{
"id": "11",
"name": "sub 1",
"stats": {
"items": 5,
},
"ions": null
},
{
"id": "22",
"name": "sub 2",
"stats": {
"items": 5,
},
"translations": null
}
],
"image": null
},
{
"id": "2",
"name": "name 2",
"thsub": [
{
"id": "33",
"name": "sub 43",
"stats": {
"items": 20,
},
"ions": null
},
{
"id": "44",
"name": "sub 76",
"stats": {
"items": 5,
},
"translations": null
}
],
"image": null
}
]
}
var thCount = [];
for(key in data.cars[0].thsub ){
if(data.cars[0].thsub[key].stats){
thCount.push(data.cars[0].thsub[key].stats.items);
}
}
console.log(thCount);
For some reason "thCount" is returning [5, 5] when the result should be: [10, 25]
where is the code going wrong?
The correct code for your problem is as pasted below:
**var count = [];
for(var i = 0; i < data.cars.length; i++){
countSum = 0;
for(key in data.cars[i].thsub){
countSum = countSum + data.cars[i].thsub[key].stats.items;
}
count.push(countSum);
}**
Try this code, will solve your problem.
You need another loop on cars.
var data = {
"cars": [{
"id": "1",
"name": "name 1",
"thsub": [{
"id": "11",
"name": "sub 1",
"stats": {
"items": 5,
},
"ions": null
}, {
"id": "22",
"name": "sub 2",
"stats": {
"items": 5,
},
"translations": null
}],
"image": null
},
{
"id": "2",
"name": "name 2",
"thsub": [{
"id": "33",
"name": "sub 43",
"stats": {
"items": 20,
},
"ions": null
}, {
"id": "44",
"name": "sub 76",
"stats": {
"items": 5,
},
"translations": null
}],
"image": null
}
]
}
var thCount = [];
for (var l = 0, m = data.cars.length; l < m; l++) {
thCount[l] = 0;
for (var i = 0, j = data.cars[l].thsub.length; i < j; i++) {
if (data.cars[l].thsub[i].stats) {
thCount[l]+=data.cars[l].thsub[i].stats.items;
}
}
}
console.log(thCount);
You should use reduce() and map() methods.Any of this using a callback function.
The reduce() method applies a function against an accumulator and each
value of the array (from left-to-right) to reduce it to a single
value.
The map() method creates a new array with the results of calling a
provided function on every element in this array.
var result=data.cars.map(function(item){
return item.thsub.reduce(function(a, b) { return a.stats.items + b.stats.items; });
});
var data = {
"cars": [
{
"id": "1",
"name": "name 1",
"thsub": [
{
"id": "11",
"name": "sub 1",
"stats": {
"items": 5,
},
"ions": null
},
{
"id": "22",
"name": "sub 2",
"stats": {
"items": 5,
},
"translations": null
}
],
"image": null
},
{
"id": "2",
"name": "name 2",
"thsub": [
{
"id": "33",
"name": "sub 43",
"stats": {
"items": 20,
},
"ions": null
},
{
"id": "44",
"name": "sub 76",
"stats": {
"items": 5,
},
"translations": null
}
],
"image": null
}
]
}
console.log(data.cars.map(function(item){
return item.thsub.reduce(function(a, b) { return a.stats.items + b.stats.items; });
}));

Categories

Resources