I have a collection based on interface below
interface ITurbine {
id: string;
name: string;
turbine: ITurbine[];
}
collection can have very deep values with same structure.
How can I extract all children in one list with one level
pseudo exampleTurbines
[
{id:1, name:test1, turbine:null},
{id:2, name:test2, turbine:[
id:3, name:test3, turbine: {id:4, name:test4, turbine:null}
id:5, name:test5, turbine: {id:6, name:test6, turbine:null}
]},
]
expected result is to extract all turbines in one array
[
{id:1, name:test1},
{id:2, name:test2},
..
{id:6, name:test6}
]
const getInLineTurbineArray = (turbines: ITurbine[]): ITurbine[] => {
let inlineTurbines: ITurbine[] = [];
inlineTurbines.forEach((turbine) => {
var test = extractTurbines(turbine);
inlineTurbines.push(test)
});
return inlineTurbines;
};
const extractTurbines = (turbines: ITurbine) => {
if (turbines.turbine) {
turbines.turbine.forEach((child) => {
return extractTurbines(child);
});
} else {
return turbines;
}
};
What you re trying to accomplish is called "flattening" and specifically flattening a Tree-like structure where a Node( a Turbine) can have 0..n leafs(other turbines). You can try to use recursion to work with structures like that.
const tree = [
{id:1, name:"test1", turbine:null},
{id:2, name:"test2", turbine:[
{id:3, name:"test3", turbine: {id:4, name:"test4", turbine:null}},
{id:5, name:"test5", turbine: {id:6, name:"test6", turbine:null}}
]
},
]
function flattenTree(tree) {
let result = [];
if(Array.isArray(tree)) {
for(const node of tree) {
result.push(
{id: node.id, name: node.name}
);
if(node.turbine) {
result = result.concat(flattenTree(node.turbine));
}
}
} else {
result.push({id: tree.id, name: tree.name})
}
return result;
}
console.log(flattenTree(tree))
while trying to come up with a solution I also saw that the example object you gave looks more like this interface:
interface ITurbine {
name: string;
id: number;
turbine?: ITurbine | ITurbine[];
}
I don't know if this was a typo on your end but the solution should probably work either way.
Related
I have two array of objects in which if property grp from arrobj1 is
same as SERVICE and ISACTIVE is true from arrobj2, then return array of object using
javascript
Tried
let result = arrobj1.filter(e=>
arrobj2.some(i=> i.ISACTIVE===true && e.grp === i.SERVICE);
);
var arrobj1=[
{
id:"SetupFS",
grp:"fs",
title: "xxx"
},
{
id:"ExtendFS",
grp:"fs",
title: "yyy"
},
{
id:"RebootServer",
grp:"os",
title: "yyy"
},
]
var arrobj2=[
{id:1, ISACTIVE:true, TASK:'SetupFS', SERVICE: "fs" },
{id:2, ISACTIVE:false, TASK:'RebootServer', SERVICE:"os" },
{id:3, ISACTIVE:false, TASK:'ExtendFS', SERVICE: "fs" },
]
Expected Result
[
{
id:"SetupFS",
grp:"fs",
title: "xxx"
}
]
You don't need filter for second item, you need to check if the item with corresponding index in arrobj1 with grp value equal to SERVICE value in arrobj2
var arrobj1=[
{
id:"SetupFS",
grp:"fs",
title: "xxx"
},
{
id:"ExtendFS",
grp:"fs",
title: "yyy"
},
{
id:"RebootServer",
grp:"os",
title: "yyy"
},
]
var arrobj2=[
{id:1, ISACTIVE:true, TASK:'SetupFS', SERVICE: "fs" },
{id:2, ISACTIVE:false, TASK:'RebootServer', SERVICE:"os" },
{id:3, ISACTIVE:false, TASK:'ExtendFS', SERVICE: "fs" },
]
let result = arrobj2.filter((item, i) =>
item.SERVICE === arrobj1[i].grp
);
console.log(result)
A simpler method
// gets two results wit the equals
let filteredList = [];
for (const item of arrobj1) {
// include TASK === item.id to get the expected answer
const inArray = arrobj2.find(e => e.ISACTIVE && e.TASK === item.id && e.SERVICE === item.grp);
if (inArray) {
filteredList.push(item)
}
}
console.log(filteredList)
with filters in the question it returns two items
e => e.ISACTIVE && e.SERVICE === item.grp
0: Object
id: "SetupFS"
grp: "fs"
title: "xxx"
1: Object
id: "ExtendFS"
grp: "fs"
title: "yyy"
Hope it helps
but if this is not what was expected, I'll delete the answer
[{id:1, name:'Chan', supervisor:''},
{id:2, name:'Wong', supervisor:'1'},
{id:3, name:'Fong', supervisor:'1'},
{id:4, name:'Ho', supervisor:'2'},
]
expected result
[
{
id: 1,
name: "Chan",
supervisor: "",
children: [
{
id: 2,
name: "Wong",
supervisor: "1",
children: [{ id: 4, name: "Ho", supervisor: "2" }]
},
{ id: 3, name: "Fong", supervisor: "1" }
]
}
]
I want to achieve this format like the above. Have tried to use lodash map and filter.
Want to know if there any fastest method to do that? Many thanks in advance.
Currently I have tried.
let children = [];
const transformedDataRecords = records.map((record) => {
let user = _.filter(records, { id: record.supervisor });
if (user.length>0) {
console.log(user[0].children);
if(!!(user[0].children)){
children = user[0].children;
}
children.push(record);
user = { ...user, children };
console.log(user);
}
});
Starting from
const staff = [{id:1, name:'Chan', supervisor:''},
{id:2, name:'Wong', supervisor:'1'},
{id:3, name:'Fong', supervisor:'1'},
{id:4, name:'Ho', supervisor:'2'},
]
This is a nice place to use Maps. You can keep a reference to each object by its ID without having to care about its location in the array:
const staffById = new Map(
// key-value pairs
staff.map(person => [person.id, person])
)
// Create the children arrays
staff.forEach(
person => {
if (person.supervisor !== "") {
// Maps do care about whether data is String or Number so we have to
// convert the supervisor field to Number to ensure they match.
const supervisorParsed = Number(person.supervisor)
const supervisorObj = staffById.get(supervisorParsed)
// Ensure there is an array to insert into
supervisorObj.children = supervisorObj.children || []
supervisorObj.children.push(person)
}
}
)
// References updated, now filter out everyone from the top-level array who is not a top-level supervisor.
const output = staff.filter(person => person.supervisor === '')
Given the following json array
const groups=[{ id:1, parent:null, groupName:'Others', maxScore:3},
{id:2, parent:null, groupName: 'Group 1', maxScore:0},
{id:3, parent:2, groupName:'Others, maxScore:2},
{id:4, parent:2, groupName:'Sub Group 1', maxScore:1}];
What would be a more performance oriented approach to sum maxScores within parents ?
It looks like a tree structure, but nodes don't have references to their children.
Im trying a map.reduce approach right now
function addMaxScoreToGroups(groups) {
return groups.map((group) => {
const newGroup = group;
if (group.parent === null && group.name !== 'Others') {
const children = groups.filter(elem => elem.parent === group.id);
if (children) {
newGroup.maxScore = children.map(x => x.maxScore)
.reduce((value, acum) => value + acum);
}
}
return newGroup;
});
}
Expected result would be
const groups=[{ id:1, parent:null, groupName:'Others', maxScore:3},
{id:2, parent:null, groupName: 'Group 1', maxScore:0,maxPosibleScore:3},
{id:3, parent:2, groupName:'Others, maxScore:2},
{id:4, parent:2, groupName:'Sub Group 1', maxScore:1}];
Iterate the with Array.reduce(). For each object create a new entry (or update existing one in case of parent) by cloning the object using object spread (or Object.assign().
If the object has a parent, create the parent if it doesn't exist, and add/update the maxPossibleScore.
Convert back to array using Object.values().
const groups=[{ id:1, parent:null, groupName:'Others', maxScore:3}, {id:3, parent:2, groupName:'Others', maxScore:2}, {id:4, parent:2, groupName:'Sub Group 1', maxScore:1}, {id:2, parent:null, groupName: 'Group 1', maxScore:5}];
const ms = 'maxScore';
const mps = 'maxPossibleScore';
const result = Object.values(groups.reduce((r, o) => {
// clone
const c = { ...o };
// if current parent initalized before, update maxPossibleScore
if(!o.parent && r[o.id]) c[mps] = r[o.id][mps] + c[ms];
r[o.id] = c;
if(o.parent) {
// if parent doesn't exist init
r[o.parent] = r[o.parent] || {};
// if parent.maxPossibleScore doesn't exist init it
if(!(mps in r[o.parent])) {
r[o.parent][mps] = r[o.parent][ms] || 0;
}
r[o.parent][mps] += o[ms];
}
return r;
}, {}));
console.log(result);
I have a json array like this:
[
{id:1, another_id:1},
{id:2, another_id:1},
{id:3, another_id:2}
]
Is it possible to divide this into json arrays based on the key another_id. In this case two json arrays should be created like this
jsonArr1 = [
{id:1, another_id:1},
{id:2, another_id:1}
]
jsonArr2 = [
{id:3, another_id:2}
]
another_id will be varying. Please help guys
If you do not know how many different result arrays you will have, you should not try to make a variable for each of them. Instead put them in an object, where each object property corresponds to a single possible value of another_id, and the value for it is the corresponding array.
You can achieve that with reduce:
var data = [{id:1, another_id:1},{id:2, another_id:1},{id:3, another_id:2}];
var result = data.reduce( (acc, obj) => {
acc[obj.another_id] = acc[obj.another_id] || [];
acc[obj.another_id].push(obj);
return acc;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you want different variables then you can build a function which will return filtered array based on the passed value. This will use Array.filter()
function formatData(check) {
var data = [{
id: 1,
another_id: 1
},
{
id: 2,
another_id: 1
},
{
id: 3,
another_id: 2
}
];
return data.filter(el => el.another_id === check);
}
jsonArr1 = formatData(1);
jsonArr2 = formatData(2);
console.log(jsonArr1);
console.log(jsonArr2);
I hope the code below will work for you. As this will create two separate json arrays Arr1 for id and Arr2 for another_id
data = [
{id:1, another_id:1},
{id:2, another_id:1},
{id:3, another_id:2}
];
console.log(data);
var Arr1 = [];
var Arr2 = [];
for(var i in data){
Arr1.push(data[i].id);
Arr2.push(data[i].another_id);
}
I have an array like this
$scope.dogs = [
{ id: 1, breed: 'German Shepherd' },
{ id: 2, breed: 'Collie' }
]
And a second array like this:
$scope.owners = [
{ name: 'Mary', breedowned: 'German Shepherd' },
{ name: 'Bill', breedowned: 'German Shepherd' },
{ name: 'Bob', breedowned: 'Collie' }
]
I want to push the list of owners into the list of dogs like so basically creating:
$scope.dogs = [
{ id: 1, breed: 'German Shepherd', owners: [...] }
]
I tried to use forEach and push the owners into the dogs array, but it does not work.
angular.forEach($scope.dogs, function (value, key) {
for (x = 0; x < $scope.owners.length; x++) {
if ($scope.owners[i].breedowned == value.breed) {
$scope.dogs[key].owners.push($scope.owners[i])
}
}
});
Thanks for any help!
If you don't want any form of dependency, just use Array.prototype.push.apply, this way:
Array.prototype.push.apply($scope.owners, $scope.dogs);
You didnt mention any errors, but I see an issue with you missing var in front of the x in the for loop, and also owners is not initialized in the dog object. Here's a consistent nested loop solution:
angular.forEach($scope.dogs, function (dog) {
angular.forEach($scope.owners, function (owner) {
if (owner.breedowned == dog.breed) {
dog.owners = dog.owners || []
dog.owners.push(owner)
}
})
})
Here a better solution that only goes through the owners array once and only through the dogs array once.
var tracker = $scope.owners.reduce(function(trackerObj, owner){
var breedowned = owner.breedowned;
trackerObj[breedowned] = trackerObj[breedowned] || [];
trackerObj[breedowned].push(owner);
return trackerObj;
}, {});
$scope.dogs.forEach(function(dog){
dog.owners = tracker[dog.breed];
});