Angular JS Looping through a multidimensional array in the controller - javascript

Currently I have my scope set out in the following format.
$scope.test = [
{
group: 'Group1',
groupID:0,
items: [
{id: 0, description: 'Item 1', error: true, groupID:0},
{id: 1, description: 'Item 2', error: true, groupID:0},
{id: 2, description: 'Item 3', error: true, groupID:0},
{id: 3, description: 'Item 4', error: true, groupID:0}
]
},
{
group: 'Group2',
groupID:1,
items: [
{id: 0, description: 'Item 1', error: true, groupID:1},
{id: 1, description: 'Item 2', error: true, groupID:1},
{id: 2, description: 'Item 3', error: true, groupID:1},
{id: 3, description: 'Item 4', error: true, groupID:1}
]
},
{
group: 'Group3',
groupID:2,
items: [
{id: 0, description: 'Item 1', error: true, groupID:2},
{id: 1, description: 'Item 2', error: true, groupID:2},
{id: 2, description: 'Item 3', error: true, groupID:2},
{id: 3, description: 'Item 4', error: true, groupID:2}
]
}
What I want is too loop through all the item objects in each parent object inside the controller and get a total of all items which the value 'error' is equal to true and alert this total.
Currently inside the controller I can only loop through the 1st level objects in the array, so can only access 'group' and 'groupID'.

function myCtrl($scope) {
$scope.test = [{
group: 'Group1',
groupID: 0,
items: [{
id: 0,
description: 'Item 1',
error: true,
groupID: 0
}, {
id: 1,
description: 'Item 2',
error: true,
groupID: 0
}, {
id: 2,
description: 'Item 3',
error: true,
groupID: 0
}, {
id: 3,
description: 'Item 4',
error: true,
groupID: 0
}]
}, {
group: 'Group2',
groupID: 1,
items: [{
id: 0,
description: 'Item 1',
error: true,
groupID: 1
}, {
id: 1,
description: 'Item 2',
error: true,
groupID: 1
}, {
id: 2,
description: 'Item 3',
error: true,
groupID: 1
}, {
id: 3,
description: 'Item 4',
error: true,
groupID: 1
}]
}, {
group: 'Group3',
groupID: 2,
items: [{
id: 0,
description: 'Item 1',
error: true,
groupID: 2
}, {
id: 1,
description: 'Item 2',
error: true,
groupID: 2
}, {
id: 2,
description: 'Item 3',
error: true,
groupID: 2
}, {
id: 3,
description: 'Item 4',
error: false,
groupID: 2
}]
}];
$scope.errors = [];
function innerLoop(obj) {
return function (items) {
for (var i = 0; i < items.length; i++) {
if (items[i].error) {
$scope.errors.push(items[i].error);
}
}
}(obj.items);
}
function loop(obj) {
for (var i = 0; i < obj.length; i++) {
innerLoop(obj[i]);
}
return $scope.errors;
}
loop($scope.test);
alert($scope.errors.length);
}

You can use nested ng-repeat in view
see example
function myCtrl($scope){
$scope.multi = [1,2,[3,4,5]] ;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-controller="myCtrl">
<div ng-repeat='i in multi'>
<span ng-hide="i.length>1">{{i}}</span>
<div ng-repeat='i2 in i'>
{{i2}}
</div>
</div>

Related

Can you transform a list to a tree where items in the list have children but no depth or parent?

Data looks as follows:
Each node has a unique id (
Nodes have a children key which is either null or an array of ids.
Nodes can have one parent
Nodes do not have a parent or depth reference
Input:
const items = [
{
id: 1,
name: 'Item 1',
children: [ 2, 3 ]
},
{
id: 2,
name: 'Item 2',
children: null
},
{
id: 3,
name: 'Item 3',
children: null
},
{
id: 4,
name: 'Item 4',
children: [ 5 ]
},
{
id: 5,
name: 'Item 5',
children: [ 6 ]
},
{
id: 6,
name: 'Item 6',
children: null
},
}
]
Expected Output:
const tree = [
{
id: 1,
name: 'Item 1',
children: [
{
id: 2,
name: 'Item 2',
children: null
},
{
id: 3,
name: 'Item 3',
children: null
},
]
},
{
id: 4,
name: 'Item 4',
children: [
{
id: 5,
name: 'Item 5',
children: [
{
id: 6,
name: 'Item 6',
children: null
}
]
}
]
}
]
If this is in fact possible, would love to 1) see how it is done and 2) see if there are any libraries that handle this use case.
The resulting structure is more a forest than a tree, as not all nodes are connected and you have multiple "roots".
You can first key the nodes by their id in a Map, and then iterate all children arrays to replace their contents by the corresponding items found in the Map. At the same time keep track of all the children, so that at the end you can identify which items are not children, and therefore belong in the result array:
const items = [{id: 1,name: 'Item 1',children: [ 2, 3 ]},{id: 2,name: 'Item 2',children: null},{id: 3,name: 'Item 3',children: null},{id: 4,name: 'Item 4',children: [ 5 ]},{id: 5,name: 'Item 5',children: [ 6 ]},{id: 6,name: 'Item 6',children: null},];
const map = new Map(items.map(item => [item.id, item]));
const children = new Set;
for (const item of items) {
if (!item.children) continue;
for (const id of item.children) children.add(id);
item.children = item.children?.map(id => map.get(id));
}
const forest = items.filter(({id}) => !children.has(id));
console.log(forest);

JavaScript: Rebuild Array of Objects using Reduce

I have an array of objects that I'm trying to rebuild without any success:
const data = [
{
ID: 1,
TemplateName: 'Template 1',
TemplateCategory: 'Category A',
},
{
ID: 2,
TemplateName: 'Template 2',
TemplateCategory: 'Category A',
},
{
ID: 3,
TemplateName: 'Template 3',
TemplateCategory: 'Category B',
},
]
I have the below code which produces the following undesired result:
result = [...data
.reduce((acc, {TemplateCategory, TemplateName, ID}) => {
const group = acc.get(TemplateCategory)
group ? group.options.push(ID, TemplateName) : acc.set(TemplateCategory, {TemplateCategory, "options":[ID, TemplateName]})
return acc
}, new Map)
.values()
]
console.log(result) // undesired result:
[
{
TemplateCategory: 'Category A',
options: [1, 'Template 1', 2, 'Template 2']
},
{
TemplateCategory: 'Category B',
options: [3, 'Template 3']
}
]
I am stuck on trying to convert options to an Array of Objects with value and label as properties. Also im struggling trying to reword TemplateCategory property to label.
My desired result is:
[
{
label: 'Category A',
options: [
{
value: 1,
label: 'Template 1'
},
{
value: 2,
label: 'Template 2'
}
]
},
{
label: 'Category B',
options: [
{
value: 3,
label: 'Template 3'
}
]
}
]
TIA
Like this
const data = [
{
ID: 1,
TemplateName: 'Template 1',
TemplateCategory: 'Category A',
},
{
ID: 2,
TemplateName: 'Template 2',
TemplateCategory: 'Category A',
},
{
ID: 3,
TemplateName: 'Template 3',
TemplateCategory: 'Category B',
},
]
const result = [...data
.reduce((acc, {TemplateCategory, TemplateName, ID}) => {
const group = acc.get(TemplateCategory)
group ? group.options.push({value: ID, label: TemplateName}) : acc.set(TemplateCategory, {label: TemplateCategory, "options":[{value: ID, label: TemplateName}]})
return acc
}, new Map)
.values()
]
console.log(result) // undesired result:

Remove object from array in another array

I have the following data structure
menu: [ { id: 1, title: 'Test 1', children: [] },
{ id: 2, title: 'Test 2', children: [
{ id: 5, title: 'Test 5', children: [] },
{ id: 6, title: 'Test 6', children: [] },
{ id: 7, title: 'Test 7', children: [] },
{ id: 8, title: 'Test 8', children: [] },
] },
{ id: 3, title: 'Test 3', children: [
{ id: 9, title: 'Test 9', children: [] },
{ id: 10, title: 'Test 10', children: [] },
{ id: 11, title: 'Test 11', children: [] },
{ id: 12, title: 'Test 12', children: [] },
] },
{ id: 4, title: 'Test 4', children: [] },
]
How can remove object with Title 'Test 5'? Or from sub array in children arr?
onDeleteClick(item) {
const menuCopy = JSON.parse(JSON.stringify(this.menu));
const index = menuCopy.indexOf(item);
if (index !== -1) {
menuCopy.splice(index, 1);
} else {
menuCopy.map((el) => {
if (el.children.length) {
el.children.map((child) => {
if (child.Id === item.Id) {
console.log(child);
}
});
}
});
}
this.setMenu(menuCopy);
}
I am stuck at this point. I think that here should be used recursion but i have no idea how to implement this.
const menu = [ { id: 1, title: 'Test 1', children: [] },
{ id: 2, title: 'Test 2', children: [
{ id: 5, title: 'Test 5', children: [] },
{ id: 6, title: 'Test 6', children: [
{ id: 5, title: 'Test 5', children: [] },
{ id: 7, title: 'Test 7', children: [] },
{ id: 8, title: 'Test 8', children: [] }
] },
{ id: 7, title: 'Test 7', children: [] },
{ id: 8, title: 'Test 8', children: [] },
] },
{ id: 3, title: 'Test 3', children: [
{ id: 9, title: 'Test 9', children: [] },
{ id: 10, title: 'Test 10', children: [] },
{ id: 11, title: 'Test 11', children: [] },
{ id: 12, title: 'Test 12', children: [] },
] },
{ id: 4, title: 'Test 4', children: [] },
];
const excludeChildrenFromTitle = (arr, excludedChildTitle) => {
return arr.map((item) => {
const children = excludeChildrenFromTitle(item.children.filter((child) => child.title !== excludedChildTitle), excludedChildTitle);
return {
...item,
children
}
});
};
console.log(excludeChildrenFromTitle(menu, 'Test 5'))
Using a simple map for the whole menu array and then filtering every children array from each menu item can do the job.
I have updated the answer to remove the excluded child from sub array too.
You can filter first each first-level element and then second-level with map:
var l = [{ id: 1, title: 'Test 1', children: [] }, { id: 2, title: 'Test 2', children: [ { id: 5, title: 'Test 5', children: [] }, { id: 6, title: 'Test 6', children: [] }, { id: 7, title: 'Test 7', children: [] }, { id: 8, title: 'Test 8', children: [] }, ] }, { id: 3, title: 'Test 3', children: [ { id: 9, title: 'Test 9', children: [] }, { id: 10, title: 'Test 10', children: [] }, { id: 11, title: 'Test 11', children: [] }, { id: 12, title: 'Test 12', children: [] }, ] }, { id: 4, title: 'Test 4', children: [] },
{ id: 5, title: 'Test 5', children: [] }, ];
const removeTitleByValue = (arr, titleValue) => {
return arr
.filter(e => e.title !== titleValue)
.map((e2) => {
const children = e2.children.filter((ch) => ch.title !== titleValue);
return { ...e2, children }
});
};
console.log(removeTitleByValue(l, 'Test 5'))

How to get number of items in array which have the same "parent" element?

I gave an array of items which looks like this
const array = [{
type: 'section',
name: 'name 1',
id: '1'
},
{
type: 'item',
name: 'item 1',
data: {
section: {
name: 'name 1',
id: '1'
}
}
},
{
type: 'item',
name: 'item 2',
data: {
section: {
id: '1',
name: 'name 1'
}
}
},
{
type: 'section',
name: 'name 2',
id: '3'
},
{
type: 'item',
name: 'item 1',
data: {
section: {
id: '3',
name: 'name 2'
}
}
},
{
type: 'section',
name: 'name 3'
}, {
type: 'section',
name: 'name 4'
},
]
I need to count and add to each object of type section the number of items that it has (numberOfItems: 3). The array is built that way that each section has its items follow it.
I also have a separate array of items which basically looks like that
const items = [{
'item_id: {
...
section: {
id: 'section_id'
name: 'section_name'
}
}]
I'm not really sure how to even start.
If I have understood your problem correctly then this code should do it:
const array = [
{
type: 'section',
name: 'name 1',
id: '1'
},
{
type: 'item',
name: 'item 1',
data: {
section: {
name: 'name 1',
id: '1'
}
}
},
{
type: 'item',
name: 'item 2',
data: {
section: {
id: '1',
name: 'name 1'
}
}
},
{
type: 'section',
name: 'name 2',
id: '3'
},
{
type: 'item',
name: 'item 1',
data: {
section: {
id: '3',
name: 'name 2'
}
}
},
{
type: 'section',
name: 'name 3'
}, {
type: 'section',
name: 'name 4'
},
];
let section;
for (let item of array) {
console.log(item);
if (item.type === "section") {
section = item;
section.numberOfItems = 0;
} else if (item.type === "item" && section) {
section.numberOfItems++;
}
}
It steps through all elements, keeps a record when it finds a section and increments the count of the previously found section when it finds an item.

How to count the number of items of a parent array by its child array object property

Supposed i have an array of personnel which has many offices.
var personnel = [
{
id: 1,
name: 'John Doe',
offices: [
{
id: 1,
name: 'Office 1',
isAssigned: false
},
{
id: 2,
name: 'Office 2',
isAssigned: true
},
{
id: 3,
name: 'Office 3',
isAssigned: false
}
]
},{
id: 2,
name: 'Jerry Smith',
offices: [
{
id: 2,
name: 'Office 2',
isAssigned: true,
},
{
id: 3,
name: 'Office 3',
isAssigned: false,
}
]
}
]
How can i get the count of the personnel which is assigned in a particular office? Or how many is assigned in office 2? Result: 2
Here a version that destructures the arguments:
personnel.filter( ({offices}) =>
offices.some( ({name, isAssigned}) => isAssigned && name === office)
).length
function countInOffice(personnel, office) {
return personnel.filter( ({offices}) =>
offices.some( ({name, isAssigned}) => isAssigned && name === office)
).length
}
var personnel = [
{
id: 1,
name: 'John Doe',
offices: [
{
id: 1,
name: 'Office 1',
isAssigned: false
},
{
id: 2,
name: 'Office 2',
isAssigned: true
},
{
id: 3,
name: 'Office 3',
isAssigned: false
}
]
},{
id: 2,
name: 'Jerry Smith',
offices: [
{
id: 2,
name: 'Office 2',
isAssigned: true,
},
{
id: 3,
name: 'Office 3',
isAssigned: false,
}
]
}
];
var count = countInOffice(personnel, 'Office 2');
console.log(count);
Using Array.prototype.filter() and Array.prototype.some():
const office2Personnel = personnel.filter(p =>
p.offices.some(o => o.name === 'Office 2' && o.isAssigned));
console.log(office2Personnel.length);
const personnel = [
{
id: 1,
name: 'John Doe',
offices: [
{
id: 1,
name: 'Office 1',
isAssigned: false
},
{
id: 2,
name: 'Office 2',
isAssigned: true
},
{
id: 3,
name: 'Office 3',
isAssigned: false
}
]
},{
id: 2,
name: 'Jerry Smith',
offices: [
{
id: 2,
name: 'Office 2',
isAssigned: true,
},
{
id: 3,
name: 'Office 3',
isAssigned: false,
}
]
}
];
const office2Personnel = personnel.filter(p => p.offices.some(o => o.name === 'Office 2' && o.isAssigned));
console.log(office2Personnel.length);
To get total counts(statistics for all offices: "how many employees are assigned to each office") use the following approach:
var personnel = [{id: 1,name: 'John Doe',offices: [{id: 1,name: 'Office 1',isAssigned: false},{id: 2,name: 'Office 2',isAssigned: true},{id: 3,name: 'Office 3',isAssigned: false}]},{id: 2,name: 'Jerry Smith',offices: [{id: 2,name: 'Office 2',isAssigned: true,},{id: 3,name: 'Office 3',isAssigned: false,}]}
],
counts = personnel.reduce(function (r, o) {
o.offices.forEach(function (i) {
(r[i.name])? ++r[i.name] : r[i.name] = 1;
});
return r;
}, {});
console.log(counts);
Iterating over the assigned offices and adding the count to an object
This will get a count for each office
var personnel = [{
id: 1,
name: 'John Doe',
offices: [{
id: 1,
name: 'Office 1',
isAssigned: false
},
{
id: 2,
name: 'Office 2',
isAssigned: true
},
{
id: 3,
name: 'Office 3',
isAssigned: false
}
]
}, {
id: 2,
name: 'Jerry Smith',
offices: [{
id: 2,
name: 'Office 2',
isAssigned: true,
},
{
id: 3,
name: 'Office 3',
isAssigned: false,
}
]
}];
let offices = {};
personnel.forEach(p=>{
p.offices.filter(o=>o.isAssigned).forEach(o=>{
offices[o.name] = offices[o.name] || 0;
offices[o.name]++;
});
});
console.log(offices);
let officeToCheck = "Office 2";
console.log(offices[officeToCheck]);
If you need to directly get the count for a specific office
var personnel = [{
id: 1,
name: 'John Doe',
offices: [{
id: 1,
name: 'Office 1',
isAssigned: false
},
{
id: 2,
name: 'Office 2',
isAssigned: true
},
{
id: 3,
name: 'Office 3',
isAssigned: false
}
]
}, {
id: 2,
name: 'Jerry Smith',
offices: [{
id: 2,
name: 'Office 2',
isAssigned: true,
},
{
id: 3,
name: 'Office 3',
isAssigned: false,
}
]
}];
let count = personnel.filter(p=>p.offices.some(o=>o.name==='Office 2'&&o.isAssigned)).length;
console.log(count);
You could use two nested Array#reduce and check for the given name.
function getCount(name) {
return personnel.reduce(function (r, a) {
return a.offices.reduce(function (s, b) {
return s + (b.name === name);
}, r);
}, 0);
}
var personnel = [{ id: 1, name: 'John Doe', offices: [{ id: 1, name: 'Office 1', isAssigned: false }, { id: 2, name: 'Office 2', isAssigned: true }, { id: 3, name: 'Office 3', isAssigned: false }] }, { id: 2, name: 'Jerry Smith', offices: [{ id: 2, name: 'Office 2', isAssigned: true, }, { id: 3, name: 'Office 3', isAssigned: false, }] }],
result = getCount('Office 2');
console.log(result);
If you have only one single name in one office, you could use Array#some and exit if one name is found.
function getCount(name) {
return personnel.reduce(function (r, a) {
return r + a.offices.some(function (b) {
return b.name === name;
});
}, 0);
}
var personnel = [{ id: 1, name: 'John Doe', offices: [{ id: 1, name: 'Office 1', isAssigned: false }, { id: 2, name: 'Office 2', isAssigned: true }, { id: 3, name: 'Office 3', isAssigned: false }] }, { id: 2, name: 'Jerry Smith', offices: [{ id: 2, name: 'Office 2', isAssigned: true, }, { id: 3, name: 'Office 3', isAssigned: false, }] }],
result = getCount('Office 2');
console.log(result);
ES6
function getCount(name) {
return personnel.reduce((r, a) => r + a.offices.some(b => b.name === name), 0);
}
var personnel = [{ id: 1, name: 'John Doe', offices: [{ id: 1, name: 'Office 1', isAssigned: false }, { id: 2, name: 'Office 2', isAssigned: true }, { id: 3, name: 'Office 3', isAssigned: false }] }, { id: 2, name: 'Jerry Smith', offices: [{ id: 2, name: 'Office 2', isAssigned: true, }, { id: 3, name: 'Office 3', isAssigned: false, }] }],
result = getCount('Office 2');
console.log(result);

Categories