I'm creating a website to interact with courses and their prerequisites.
This is for free, isn't a homework and I'm using this project to learn MERN
I did this tree schema which represents all courses and their dependencies.
Notice that a course can have more than one prerequisite.
So I'm having rough time translating this into TDA. I have been trying with multi parents tree but I have no results due to:
Can't recreate multiparents into one node
Can't traverse a tree with implementations so I can't know which courses are available
I was using this Tree/Node definition in JS. I didn't try with Python but I have no problem in using another language even when I know language isn't the problem.
class Tree {
constructor(root) {
this._root = root || null;
}
_traverse(callback) {
const self = this;
function goThrough(node) {
callback(node);
node.children.forEach((child) => {
goThrough(child);
});
}
goThrough(this._root);
}
_addNode(value, parentValue) {
const newNode = {
value,
children: []
};
if (this._root === null) {
this._root = newNode;
return;
}
this._traverse((node) => {
if (node.value === parentValue) {
node.children.push(newNode);
}
});
}
_removeNode(value) {
this._traverse((node) => {
node.children.forEach((childNode, index) => {
if (childNode.value === value) {
node.children.splice(index, 1);
}
});
})
}
_search(value) {
let returnNode = 'Not Found';
this._traverse((node) => {
if (node.value === value) {
returnNode = node;
}
});
return returnNode;
}
_displayLeafs(parentValue) {
const parentNode = typeof parentValue === 'string' ? this._search(parentValue) : parentValue ;
let leafsRet = [];
if (parentValue.children && !parentValue.children.length) {
return parentValue;
}
parentNode.children.forEach((child) => {
leafsRet.push(this._displayLeafs(child));
});
return leafsRet.flat();
}
}
class Node {
constructor(value, children) {
this.value = value;
this.children = children;
}
}
const malla = new Tree();
malla._addNode('root');
malla._addNode('CMB1000','root');
malla._addNode('CMB1001','root');
malla._addNode('CIT1000','root');
malla._addNode('FIC1000','root');
malla._addNode('CBQ100','root');
malla._addNode('CIT1010','CIT1000');
console.log(malla._displayLeafs('root'));
I didn't follow inserting new nodes because I'm not getting results as expected.
Can anyone give a hint about a tree implemention that may I know? Or maybe a better approach to this situation?
Thanks for your time!
Related
I wrote the small class below for playing with a binary tree. Is it possible to do depth first search with out recursion?
It looks like you might need a queue to remember where you are in the traversal, but I'm not sure.
Is there a way to do it with out adding another data structure?
class Node {
constructor(data) {
this.left = null;
this.right = null;
this.data = data;
}
}
class BinaryTree {
constructor(data) {
this.root = new Node(data);
this.makeTree();
}
makeTree(){
this.insert(15);
this.insert(5);
this.insert(7);
this.insert(3);
this.insert(20);
this.insert(13);
}
insert(data) {
let iter = this.root;
let node = new Node(data);
while(iter) {
if(iter.left && (node.data < iter.data)){
iter = iter.left;
}
if(!iter.left && (node.data < iter.data)){
iter.left = node;
return;
}
if(iter.right && (node.data >= iter.data)){
iter = iter.right;
}
if(!iter.right && (node.data >= iter.data)){
iter.right = node;
return;
}
}
}
df() {
function dfInner (node) {
if(node === null) {
return node;
}
// pre order
dfInner(node.left);
// in order
dfInner(node.right);
// post order
}
dfInner(this.root);
}
}
const tree = new BinaryTree(10);
tree.df();
have a problem with the implamantation search in the multiple tree view.
Can check the code in the link.
In the group we have childGroup and list, we have to search lists and groups names. (check data in the data.js file)
Think the problem is somewhere here.
const search = (items, term) => {
return items.reduce((acc, item) => {
if (contains(item.name, term)) {
acc.push(item);
} else if (item.childGroupList?.length) {
let newGroupsItems = search(item.childGroupList, term);
if (newGroupsItems?.length) {
item.childGroupList = newGroupsItems;
acc.push(item);
}
} else if (item.list?.length) {
let newListItems = search(item.list, term);
if (newListItems?.length) {
item.list = newListItems;
acc.push(item);
}
}
return acc;
}, []);
};
There were a few issues in your recursive search.
Your if, else if, else if chain should be independent. They should really be three if blocks.
You need to push the result only after when all the searches are done at a certain level. I got a copy of the item and make childGroupList and list properties empty([]) initially and update them when a search is successful.
Try like this.
// main search function, here is the problem
const search = (items, term) => {
return items.reduce((acc, item) => {
let itemCopy = JSON.parse(JSON.stringify(item));
itemCopy.childGroupList = [];
itemCopy.list = [];
let found = false;
if (contains(item.name, term)) {
found = true;
}
if (item.childGroupList?.length) {
let newGroupsItems = search(item.childGroupList, term);
if (newGroupsItems?.length) {
found = true;
itemCopy.childGroupList = newGroupsItems;
}
}
if (item.list?.length) {
let newListItems = search(item.list, term);
if (newListItems?.length) {
found = true;
itemCopy.list = newListItems;
}
}
if (found) {
acc.push(itemCopy);
}
return acc;
}, []);
};
Code sandbox => https://stackblitz.com/edit/react-rmrlj1?file=src%2FApp.js
I needed a sort of aggregate class that forwards it's method calls to all of it's constituents. This is what I wrote:
class CompositeSpritesheet {
sprites = []; // collect Spritesheet objects
draw() {
this.sprites.forEach(s => (s.draw.apply(s, arguments)))
}
init() {
this.sprites.forEach(s => (s.init.apply(s, arguments)))
}
nextStep() {
this.sprites.forEach(s => (s.nextStep.apply(s, arguments)))
}
// ... for all possible functions
}
let board = new Spritesheet("a");
let text = new Spritesheet("b");
let composite = new CompositeSpritesheet();
composite.sprites.push(wood,text);
composite.draw(20,30);
This code is really repetitive, not exhaustive, might fail for unexpected function calls, and most importantly just really icky. Is there a way I can generalize this and maybe write a higher-order class that generates such compositions at whim?
In other words, is there a mechanism in JavaScript that lets me replicate the following pseudocode's behavior?
class CompositeSpritesheet{
sprites = [];
*("fn", args){
this.sprites.forEach(s => (s[fn]?.apply(s, arguments)))
}
}
This could be a dumb question, but I find JavaScript flexible in many unexpected ways that I haven't been able to completely wrap my head around. I feel like it could possible in some form or another.
If you have ideas that don't completely answer the question but are still related to the solution, feel free to post them as comments.
... something like this ..?
class CompositeSpritesheet {
constructor(...sprites) {
let spriteList = [].concat(...sprites);
this.addSprites = (...sprites) => {
spriteList = spriteList.concat(...sprites);
};
this.execute = (actionName, ...args) => {
spriteList.forEach(sprite => {
const action = sprite[actionName];
if (typeof action === 'function') {
action.apply(sprite, args);
}
});
};
}
}
let board = new Spritesheet("a");
let text = new Spritesheet("b");
let composite = new CompositeSpritesheet();
composite.addSprites(board, text);
composite.execute('draw', 20, 30);
Edit / Second Iteration
The next provided approach is a generic one. Knowing that at any time every internally listed Spritesheet type does feature the same set of methods, a newly introduced CompositeSpritesheet type does now detect all accessible keys of its first listed Spritesheet element, either at construction time or as soon as such (an) element(s) will be concatenated to the composite type's internally managed spritesheet-list. Then, while iterating this key-list, for every found spritesheet-method a corresponding forwarder method gets created dynamically.
... working example code ...
function isBooleanValue(type) {
return (typeof type === 'boolean');
}
function isNumberValue(type) {
return (typeof type === 'number');
}
function isStringValue(type) {
return (typeof type === 'string');
}
function isSymbol(type) {
return (typeof type === 'symbol');
}
function isPrimitive(type) {
return (
isBooleanValue(type)
|| isNumberValue(type)
|| isStringValue(type)
|| isSymbol(type)
);
}
function isObject(type) {
return (!!type && (typeof type === 'object'));
}
function isFunction(type) {
const functionType = 'function';
return (
(typeof type === functionType)
&& (typeof type.call === functionType)
&& (typeof type.apply === functionType)
);
}
/**
* - recursively collect a list of a type’s accessible keys
* that also might be inherited, but that are neither keys
* of `Object.prototype` nor keys of `Function.prototype`.
*/
function getAllAccessiblePropertyNames(type, keyList) {
// default return value.
keyList = (keyList || []);
// accept primitive data types as well.
if (isPrimitive(type)) {
type = Object(type);
}
// undefined and null value are kept out.
if (isObject(type)) {
keyList = keyList.concat(
Object.keys(type)
).concat(
Object.getOwnPropertyNames(type)
);
const protoType = (isFunction(type.constructor) && type.constructor.prototype);
if (protoType && (protoType !== Object.prototype)) {
if (protoType === protoType.constructor.prototype) {
keyList = keyList.concat(
Object.keys(protoType)
).concat(
Object.getOwnPropertyNames(protoType)
);
} else {
keyList = getAllAccessiblePropertyNames(protoType, keyList);
}
}
const proto = type.__proto__;
if ((isObject(proto) || isFunction(proto)) && (proto !== Object.prototype)) {
if (proto === proto.__proto__) {
keyList = keyList.concat(
Object.keys(proto)
).concat(
Object.getOwnPropertyNames(proto)
);
} else {
keyList = getAllAccessiblePropertyNames(proto, keyList);
}
}
}
return [...(new Set(keyList))].filter(key => !(/^\d+$/).test(key));
}
function isEmptyList(type) {
return ((type = Object(type)) && ('length' in type) && (Array.from(type).length === 0));
}
function withSpritesheetForwarderMethods(sharedState) {
// guard.
if (sharedState.hasForwarderMethods || isEmptyList(sharedState.spritesheetList)) {
// either does already feature all methods or there is still nothing to work with.
return;
}
const compositeType = this;
const spritesheetType = sharedState.spritesheetList[0];
getAllAccessiblePropertyNames(spritesheetType).forEach((key) => {
if (isFunction(spritesheetType[key])) {
// apply spritesheet forwarder method.
compositeType[key] = function (...args) {
sharedState.spritesheetList.forEach(
(spritesheet) => spritesheet[key].apply(spritesheet, args)
);
}
}
});
sharedState.hasForwarderMethods = true;
}
function withSpritesheetListManagement(sharedState) {
const compositeType = this;
compositeType.addSpritesheets = (...spritesheets) => {
sharedState.spritesheetList = sharedState.spritesheetList.concat(...spritesheets);
// ... either applicable latest at concat time ...
withSpritesheetForwarderMethods.call(compositeType, sharedState);
};
// ... or (preferably) already applicable at construction time.
withSpritesheetForwarderMethods.call(compositeType, sharedState);
}
class CompositeSpritesheet {
constructor(...spritesheets) {
const compositeType = this;
const sharedState = {
hasForwarderMethods: false,
spritesheetList: [].concat(...spritesheets)
};
withSpritesheetListManagement.call(compositeType, sharedState);
}
}
class Spritesheet {
constructor(name) {
this.name = name;
}
draw(...args) {
console.log(`"${ this.name }" :: draw :: args : `, args);
}
}
// test 1 :: apply forwarder methods at construction time.
const board = new Spritesheet("board");
const text = new Spritesheet("text");
const forwardingAtConstructionTime = new CompositeSpritesheet([board, text]);
console.log('forwardingAtConstructionTime.draw : ', forwardingAtConstructionTime.draw);
forwardingAtConstructionTime.draw(20, 30);
// test 2 :: apply forwarder methods at concat time.
const wood = new Spritesheet("wood");
const paper = new Spritesheet("paper");
const forwardingAtConcatTime = new CompositeSpritesheet();
console.log('forwardingAtConcatTime.draw : ', forwardingAtConcatTime.draw);
forwardingAtConcatTime.addSpritesheets(wood, paper);
forwardingAtConcatTime.draw(50, 10);
console.log('forwardingAtConcatTime.draw : ', forwardingAtConcatTime.draw);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Edit / Third Iteration
The design of the 3rd iteration is much cleaner. The class implementation is straight forward. The glue code work of dynamically creating the forwarding actions is done by a factory named from with this factory also being the only method of its CompositeSpritesheet namespace. Thus, this factory needs any randomly chosen or ad hoc created Spritesheet type in order to then create an according CompositeSpritesheet type.
... working example code ...
function isBooleanValue(type) {
return (typeof type === 'boolean');
}
function isNumberValue(type) {
return (typeof type === 'number');
}
function isStringValue(type) {
return (typeof type === 'string');
}
function isSymbol(type) {
return (typeof type === 'symbol');
}
function isPrimitive(type) {
return (
isBooleanValue(type)
|| isNumberValue(type)
|| isStringValue(type)
|| isSymbol(type)
);
}
function isObject(type) {
return (!!type && (typeof type === 'object'));
}
function isFunction(type) {
const functionType = 'function';
return (
(typeof type === functionType)
&& (typeof type.call === functionType)
&& (typeof type.apply === functionType)
);
}
/**
* - recursively collect a list of a type’s accessible keys
* that also might be inherited, but that are neither keys
* of `Object.prototype` nor keys of `Function.prototype`.
*/
function getAllAccessiblePropertyNames(type, keyList) {
// default return value.
keyList = (keyList || []);
// accept primitive data types as well.
if (isPrimitive(type)) {
type = Object(type);
}
// undefined and null value are kept out.
if (isObject(type)) {
keyList = keyList.concat(
Object.keys(type)
).concat(
Object.getOwnPropertyNames(type)
);
const protoType = (isFunction(type.constructor) && type.constructor.prototype);
if (protoType && (protoType !== Object.prototype)) {
if (protoType === protoType.constructor.prototype) {
keyList = keyList.concat(
Object.keys(protoType)
).concat(
Object.getOwnPropertyNames(protoType)
);
} else {
keyList = getAllAccessiblePropertyNames(protoType, keyList);
}
}
const proto = type.__proto__;
if ((isObject(proto) || isFunction(proto)) && (proto !== Object.prototype)) {
if (proto === proto.__proto__) {
keyList = keyList.concat(
Object.keys(proto)
).concat(
Object.getOwnPropertyNames(proto)
);
} else {
keyList = getAllAccessiblePropertyNames(proto, keyList);
}
}
}
return [...(new Set(keyList))].filter(key => !(/^\d+$/).test(key));
}
const CompositeSpritesheet = (function () {
// module scope.
// lean class.
class CompositeSpritesheet {
// declare private instance field.
#list
constructor() {
// initialize private instance field.
this.#list = [];
}
// prototypal instance methods with private instance field access.
getSpritesheets() {
return [...this.#list];
}
addSpritesheets(...spritesheets) {
return [...(this.#list = this.#list.concat(...spritesheets))];
}
}
// creation helper.
function createForwardingAction(composite, key) {
composite[key] = function (...args) {
composite.getSpritesheets().forEach(
(spritesheet) => spritesheet[key].apply(spritesheet, args)
);
}
}
// factory.
function createCompositeFromSpritesheetType(dummySpritesheet) {
const composite = new CompositeSpritesheet();
getAllAccessiblePropertyNames(dummySpritesheet).forEach((key) => {
if (isFunction(dummySpritesheet[key])) {
// apply spritesheet forwarder method.
createForwardingAction(composite, key);
}
});
return composite;
}
// module export.
return {
from: createCompositeFromSpritesheetType
};
}());
class Spritesheet {
constructor(name) {
this.name = name;
}
draw(...args) {
console.log(`"${ this.name }" :: draw :: args : `, args);
}
}
// test
const composite = CompositeSpritesheet.from(new Spritesheet);
console.log('composite : ', composite);
console.log('get current spritesheet list : ', composite.getSpritesheets());
const board = new Spritesheet("board");
const text = new Spritesheet("text");
const wood = new Spritesheet("wood");
const paper = new Spritesheet("paper");
console.log('add [board, text] to list : ', composite.addSpritesheets(board, text));
composite.draw(20, 30);
composite.addSpritesheets([wood, paper]); // add via array is possible too.
console.log('get current spritesheet list : ', composite.getSpritesheets());
composite.draw(50, 10);
.as-console-wrapper { min-height: 100%!important; top: 0; }
EDIT:
Given the comments in the other answer, what you could do is to create your class and then add the functions to the prototype:
class CompositeSpritesheet {
sprites = [];
};
var functionsToCreate = ["draw", "init", "nextStep"];
for (let f of functionsToCreate) {
CompositeSpritesheet.prototype[f] = function() {
this.sprites.forEach(s => (s[f].apply(s, arguments)));
}
}
And so CompositeSpritesheet will have all the functions you want in its prototype and so you will be able to call composite.draw(20,30);.
The only thing you have to do is to fill the functionsToCreate array.
=====================
A solution I can see could be to call only one method with different arguments:
class CompositeSpritesheet {
function doSomething(action, args) {
this.sprites.forEach(s => (s[action].apply(s, args)));
}
So you can call composite.doSomething("draw", [20,30]);.
I'm trying to do the equivalent of .GroupBy(key => key.IdItem).Select(item => item.Single()); from linq to typescript.
What I've tried:
let parents = value.reduce((parents, parent) => ({
...ubc,
[parent.IdItem]: [...(parents[u.IdItem] || []), parent],
}), {}) as Array<ItemsViewModel>;
Array.prototype.map.call(parents, parent => {
if (parents.length > 1) {
throw new Error('The input sequence contains more than one element');
}
else if (!parents.length) {
throw new Error('The input sequence is empty');
}
return parent[0];
});
What am I doing wrong?
You can try it like this --
let parentsById =
value.reduce((dict, parent) => {
let { IdItem } = parent;
dict[IdItem] = dict[IdItem] || [];
dict[IdItem].push(parent);
return dict;
}, {});
let result =
Object.keys(parentsById)
.map(k => {
let parents = parentsById[k];
if(parents.length !== 1) { throw new Error("Must contain a single item"); }
return parents[0];
});
Also, if you simply want unique parents, you can use a hashet of seen IDs --
let result = [];
let seenIds = {};
for(let parent of value) {
let { IdItem } = parent;
if(!seenIds[IdItem]) {
result.push(parent);
seenIds[IdItem] = true;
}
}
I've got a data structure that looks something like this:
let tree = {
id: 1,
name: "Some Name",
children: [
{
id: 2,
name: "Child 1",
children: [...more nested objects...]
}
]
};
I've written a recursive function to find a given object within that tree, but I now need to also return the path through the tree to the object that is returned. I'm trying to figure out how to modify my search function to do this.
Search function:
_findInTree = (id, tree) => {
let result;
if (tree.id === id) {
result = tree;
} else {
for (let child of tree.children) {
if (child.id === id) { result = child; }
result = this._findInTree(id, child);
if (result) { break; }
}
}
return result;
}
You'll need the array index, so you can either track it outside the for-of and then use it on the path, or use Array#some instead (or use a boring old for).
Here's tracking the index outside the for-of — I also added an else I think was probably pretty important: :-)
_findInTree = (id, tree, path = "") => {
let result;
let index;
let rv;
if (tree.id === id) {
result = tree;
} else {
index = 0;
for (let child of tree.children) {
if (child.id === id) {
result = child;
break;
}
rv = this._findInTree(id, child, path + "[" + index + "]");
if (rv != null) {
return rv;
}
++index;
}
}
return { result, path };
};
Obviously, adjust the format of path as you see fit. (Doesn't have to be a string, for instance, could be an array.)
Here's the some solution:
_findInTree = (id, tree, path = "") => {
let result;
let rv = null;
if (tree.id === id) {
result = tree;
} else {
tree.children.some((child, index) => {
if (child.id === id) {
result = child;
return true;
}
rv = this._findInTree(id, child, path + "[" + index + "]");
if (rv) {
return true;
}
});
}
return rv || { result, path };
};
So T.J. Crowders position ended up having a bug around recording the path, and I ended up tweaking the solution to get the following, which works excellently.
_findInTree(id, tree) {
if (tree.id === id) {
let path = [tree.name];
return {result: tree, path};
} else {
for (let child of tree.children) {
let tmp = this._findInTree(id, child);
if (!_.isEmpty(tmp)) {
tmp.path.unshift(tree.name);
return tmp;
}
}
return {};
}
}
As For me, I need to change Kevin Whitaker code to this one
_findInTree(id, tree) {
if (tree.id === id) {
let path = [tree.name];
return {result: tree, path};
} else if (tree.children) { //THIS IS THE CHANGES THAT I NEED
for (let child of tree.children) {
let tmp = this._findInTree(id, child);
if (!_.isEmpty(tmp)) {
tmp.path.unshift(tree.name);
return tmp;
}
}
return {};
}
}