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
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)
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);
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
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 ));
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; });
}));