I'm using the latest version of ember-cli, ember-data, ember-localstorage-adapter, and ember.
I have a Node object which has a parent and children. Since I had issues with creating multiple relationships with the same type of object, I decided to store the parentID in a string, and the childIDs in an array of strings. However, when I create a new Node and try to add the new Node's to the parents array of IDs, the ID ends up being added to the correct parent, but also other parents.
level 1 0
/ \
level 2 1 2
| |
level 3 3 4
In a structure like this, 0, 1, and 2 all have correct child and parent IDs. However, after adding 3 and 4, node 1 and node 2's childIDs are [3, 4], instead of [3], [4] respectively.
The Array attribute:
var ArrayTransform = DS.Transform.extend({
serialize: function(value) {
if (!value) {
return [];
}
return value;
},
deserialize: function(value) {
if (!value) {
return [];
}
return value;
}
});
The insertNode code:
insert: function(elem) {
var i,
_store = elem.node.store,
newNodeJSON = elem.node.serialize();
newNodeJSON.childIds = [];
newNodeJSON.level = getNextLevel();
_store.filter('node', function(node) {
return node.get('level') === newnodeJSON.level-1;
}).then(function(prevLevelNodes) {
// if no other nodes yet
if (prevLevelNodes.toArray().length === 0) {
makeNewNode(_store, newNodeJSON, elem.node);
}
// else, generates however many nodes that are in the previous level
else {
prevLevelNodes.toArray().forEach(function(node, idx) {
newNodeJSON.parentId = node.get('id');
makeNewNode(_store, newNodeJSON, elem.node);
});
}
});
}
var makeNewNode = function(_store, newNodeJSON, node) {
console.log(newNodeJSON.parentId); // returns correct value
var newNode = _store.createRecord('node', newNodeJSON);
newNode.save();
var newNodeId = newNode.get('id');
if (newNode.get('parentId')) {
_store.find('node', newNode.get('parentId')).then(function(n) {
var cids = n.get('childIds');
console.log(newNodeId); // returns expected value
console.log(cids); // **DOESN'T RETURN AN EMPTY ARRAY**: returns array with [3,4]
cids.push(newNodeId);
console.log(n.get('childIds')); // returns array with [3,4]
n.save();
});
}
To top this off, this error happens 90% of the time, but 10% of the time it performs as expected. This seems to suggest that there's some sort of race condition, but I'm not sure where that would even be. Some places that I feel like might be causing issues: the ember-cli compilation, passing the entire _store in when making a new node, ember-data being weird, ember-localstorage-adapter being funky... no clue.
For anyone else who may have this problem in the future: the problem lies in two things.
In ArrayTransform, typically I am returning the value sans modification.
In my insert code, I'm passing the same JSON that I defined at the top of the function to makeNewNode.
This JSON contains a reference to a single childIds array; therefore, each new node that gets created uses this same reference for its childIds. Although this doesn't quite explain why the cids array wasn't empty before the push executed (perhaps this is some sort of compiler oddity or console printing lag), it explains why these both Level 3 children were in both Level 2 parents' childIds array.
tl;dr: pass by value vs pass by reference error
Related
I am just a beginner, so if the error is something too obvious, I apologize .
My two questions are:
What is this.root in our school's provided code;
How can I implement the .height method in order to measure the depth of a Tree.
The explanation:
We were provided with this code in the class:
function BinarySearchTree(value) {
this.value = value;
this.right = null;
this.left = null;
}
BinarySearchTree.prototype.add = function(value) {
let newLeaf = new BinarySearchTree(value)
if(value > this.value){
this.right === null? this.right = newLeaf : this.right.add(value)
} else {
this.left === null? this.left = newLeaf : this.left.add(value)
}
};
And we were supposed to write a method to calculate the height/depth of a binary tree. Now, while practicing, I've seen something odd. Upon creation of a new node of an empty binary tree, the first node ends up being completely empty, while it proceeds to create a new node on the left side of the first empty one. Well, not empty, but whose value is undefined. Is this a desired behavior?
let newTree = new BinarySearchTree
>undefined
newTree.add(7)
>undefined
newTree.add(3)
>undefined
newTree.add(5)
>undefined
newTree
>BinarySearchTree {value: undefined, right: null, left: BinarySearchTree}
left: BinarySearchTree {value: 7, right: null, left: BinarySearchTree}
right: null
value: undefined
[[Prototype]]: Object
Now, considering the tests are passing for .add method, obviously I may be wrong in this situation, since this is the code provided to us by the teacher in the class.
This is the code I keep finding online and the reason I am not getting far with my code for .heigth method is because I am unable to implement this.root:
function Node(val){
this.value = val;
this.left = null;
this.right = null;
}
function BinarySearchTree(){
this.root = null;
}
How should I proceed with the .height method?
If it helps, here are the tests:
describe('Binary Search Tree', function() {
var binarySearchTree;
beforeEach(function() {
binarySearchTree = new BinarySearchTree(5);
});
it('should have methods named "add", "contains", "depthFirstPre", "depthFirstIn", "depthFirstPost", "breadthFirst"', function() {
expect(binarySearchTree.add).to.be.a("function");
});
it('should add values at the correct location in the tree', function(){
binarySearchTree.add(2);
binarySearchTree.add(3);
binarySearchTree.add(7);
binarySearchTree.add(6);
expect(binarySearchTree.left.right.value).to.equal(3);
expect(binarySearchTree.right.left.value).to.equal(6);
});
it('height method should return correct height', function() {
binarySearchTree.left = new BinarySearchTree(3);
binarySearchTree.left.left = new BinarySearchTree(1);
expect(binarySearchTree.height()).to.eql(2);
binarySearchTree.left.left.right = new BinarySearchTree(2);
expect(binarySearchTree.height()).to.eql(3);
binarySearchTree.left.left.left = new BinarySearchTree(0);
expect(binarySearchTree.height()).to.eql(3);
binarySearchTree.right = new BinarySearchTree(8);
expect(binarySearchTree.height()).to.eql(3);
});
}
Again, I apologize for a long question. I was trying to write all the relevant information regarding my problem.
Happy holidays!
What is this.root in our school's provided code
Your school's template code does not manage what is the root of the tree, so this must be managed in a variable by the driver code. In the testing code this variable is named binarySearchTree, and it really is what would be called this.root in the second (2-class) implementation.
Now, while practicing, I've seen something odd. Upon creation of a new node of an empty binary tree, the first node ends up being completely empty [...] Is this a desired behavior?
No it is not desired behavior. The template code does not provide the concept of an empty binary tree. It expects you to create the tree with at least one value, which should be provided as argument to the constructor. It is not intended to leave out the argument when calling the constructor.
The 2-class implementation provides the idea of an empty tree. But the school's template code does not; you would just have to state binarySearchTree = null if you want an empty tree. But the downside is clear: you cannot use the methods of the class to add a value to that. The only way to get the first value in a tree is to call the constructor and assign the constructed object to your binarySearchTree variable. So adding the very first value to the tree requires a different approach than adding the other values. This is also what you see in the testing code: the first value is added as argument to the constructor -- which is always called with an argument -- while the other values are added by calling the add method. This is a pity and really shows the limitations of the template code.
How can I implement the .height method in order to measure the depth of a Tree.
The idea is that you use recursion:
If there is a left child, get the height of the left subtree through recursion. If there is none, use -1 as default, as it is an empty subtree, and empty trees have a height of -1. Do the same at the right side. Get the maximum of these two values, since only the higher subtree of the two determines what is the height of the tree. Finally add one to this result so to account for the current node.
BinarySearchTree.prototype.height = function() {
return 1 + Math.max(
this.left !== null ? this.left.height() : -1,
this.right !== null ? this.right.height() : -1
);
};
Again, you can only run the height method an a tree that has at least one node, because of the limitations of the school's template code.
For completeness sake, the 2-class equivalent would place the above code on the Node class, and would add a wrapper method on the BinarySearchTree class, like this:
Node.prototype.height = function() {
return 1 + Math.max(
this.left !== null ? this.left.height() : -1,
this.right !== null ? this.right.height() : -1
);
};
BinarySearchTree.prototype.height = function() {
return this.root === null ? -1 : root.height();
}
Suppose I am given an array of pairs (where pair[0] depends on pair[1]). I want to detect whether there is a cycle between any of the pair dependencies.
Cycle:
[[0,1], [1,2], [2, 1]]
Explanation: There is a cycle between at 1 -> 2 and 2 -> 1
Not a Cycle:
[[0,1], [1,2], [0, 2]]
TLDR;
The problem I am having is... once I have "detected" a loop, I cannot seem to figure out how to "return" it. The callstack coninues executing the "other" children, but I want it to stop.
You can skip to bottom (The Algorithm)
Approach:
Create a graph representation of pairs using a Map ✅
/**
* #param { array } [ [intA, intB] ] - Nested array of integers pairs
* #return { Map } (representing a Graph)
*/
function createGraph(array) {
const nodes = new Map();
// Create Verticies
array.map(pair => {
const aClass = pair[0];
const bClass = pair[1];
nodes.set(aClass, []);
nodes.set(bClass, []);
})
// Create Edges
array.map(pair => {
const aClass = pair[0];
const bClass = pair[1];
nodes.get(aClass).push(bClass);
});
return nodes;
}
And so far, it is working as expected:
// Create Graph
const array = [[0,1], [1,2], [0, 2]];
const graph = createGraph(array);
console.log(graph);
// Map(3) { 0 => [ 1, 2 ], 1 => [ 2 ], 2 => [] }
Do a DFS of all the unvisted Nodes (in the GreySet) (until we have detect a cycle). ⚠️
I used the 3 Coloured Set approach. I really liked the algorothim and truly wish to implement it. From my understanding, it goes as follows.
WhiteSet contains all nodes that have not been touched.
GreySet contains nodes that are currently being explored. Thus, in our DFS, the "parent" will remain in this set.
BlackSet will hold nodes that have already been explored (to the point where there was no cycle).
So... If we explore a child that is in the BlackSet, there is no reason for us to explore it any further (it has already been explored and it does not have any cycle anyways). However, if we come across a child that is NOT in the WhiteSet AND it exists in the GreySet, that means we have a cycle.
Here is my code, I have added the console.logs below. The problem I am having is... once I have "detected" a loop, I cannot seem to figure out how to "return" it. It continues executing as you will see.
The Algorithm:
// Create Graph
const graph = createGraph(array);
console.log(graph); // Looks Good
// Detect Cycle
// Create 3 Sets
const whtSet = new Set(graph.keys()); // Put all the Integer "node values" the set.
const grySet = new Set();
const blkSet = new Set();
const unvisitedValues = whtSet.keys(); // Iterator
while (whtSet.size > 0) {
const doesItHaveCycle = hasCycle(unvisitedValues.next().value); // Expore any unexplored nodeVal
console.log('whtSet', whtSet);
console.log('grySet', grySet);
console.log('blkSet', blkSet);
console.log('does it have a cycle', doesItHaveCycle);
}
function hasCycle(nodeVal) {
// Blackset means it has been compltely explored :)
if (blkSet.has(nodeVal)) return false;
// This means we have found a cycle
if (!whtSet.has(nodeVal) && grySet.has(nodeVal)) return true;
// Remove it from the whiteSet, into the greySet.
whtSet.delete(nodeVal);
grySet.add(nodeVal);
// Recurse Children
graph.get(nodeVal).forEach((child) => {
let doesHaveCycle = hasCycle(child);
console.log(
'doesHaveCycle result: ',
doesHaveCycle,
'when exploring nodeVal',
nodeVal,
'and child',
child
);
if (doesHaveCycle === true) return true; // RETURN THIS PLS lol
});
// If above was true, I DO NOT want it to come here, but it still does.
console.log('if above was true... shouldnt come here');
// Now, that we have explored all of the children, remove it from Grey Set...
// Add it to the Black Set
grySet.delete(nodeVal);
blkSet.add(nodeVal);
return false; // IDK
}
Results of console.log
doesHaveCycle result: true when exploring nodeVal 2 and child 1
if above was true... shouldnt come here
doesHaveCycle result: false when exploring nodeVal 1 and child 2
if above was true... shouldnt come here
doesHaveCycle result: false when exploring nodeVal 0 and child 1
if above was true... shouldnt come here
whtSet Set(0) {}
grySet Set(0) {}
blkSet Set(3) { 2, 1, 0 }
does it have a cycle false
I am 100% open revising the code, it is a mess right now. But, I still want to do it the "3 Colour Set" way.
For more context, I was trying to take a crack at this cute algorithmic problem called Course Schedule.
// Recurse Children
graph.get(nodeVal).forEach((child) => {
let doesHaveCycle = hasCycle(child);
console.log(
'doesHaveCycle result: ',
doesHaveCycle,
'when exploring nodeVal',
nodeVal,
'and child',
child
);
if (doesHaveCycle === true) return true; // RETURN THIS PLS lol
});
The command "RETURN THIS PLS lol" usually works, but this is a special case. foreach just calls the callback on each element, and ignores the return value of the callback.
The function you need is called some. It calls the given function on each element just like foreach, and returns true as soon as one of those calls returns true. (It basically tells if the callback is true for some element.) It returns false otherwise.
This is how some is used:
// Recurse Children
const cycle = graph.get(nodeVal).some((child) => {
let doesHaveCycle = hasCycle(child);
console.log(
'doesHaveCycle result: ',
doesHaveCycle,
'when exploring nodeVal',
nodeVal,
'and child',
child
);
return doesHaveCycle;
});
if (cycle) return true;
// now this obviously works
console.log('if above was true... shouldnt come here');
You also probably want to break out of the while loop once a cycle is found:
let doesItHaveCycle = false;
while (whtSet.size > 0) {
doesItHaveCycle = hasCycle(unvisitedValues.next().value); // Expore any unexplored nodeVal
if (doesItHaveCycle) break;
}
Because, we are not doing a full DFS but returning as soon as we see the cycle. This leaves the current node grey and some of its children not-visited. If we wanted to continue after detecting a cycle, we would need to visit all the children, change the current node to black, and then return if there is a cycle.
I am writing a script for Premiere pro where I can add markers in the timeline and export a still for each marker in one go. However, when I write a function to check if the still has been previously created, the functions tells me it finds the previously created still, but then still creates a new one.
So basically: Function returns true, but still executes the else{}
//checks if the frame that is about to be exported already exists
if(checkIfExist(app.project.rootItem, outputFile)){
alert("frame already exists");
}else{
//This is where the actual still gets created and imported
activeSequence.exportFramePNG(time, outputFileName);
//here the previously created item gets moved to the appropriate bin (This is working great ATM)
moveToBin(outputFile);
}
}
}
//This function is meant to check if an item exists in the project bin. It does this by looping though all the items in the array from the start.
function checkIfExist(currentItem, name){
for(var i = 0; i<currentItem.children.numItems; i++){
currentChild = currentItem.children[i];
if(currentChild.name.toUpperCase() === name.toUpperCase()){
alert("Found: " + currentChild.name);
return true;
}if(currentChild.type == ProjectItemType.BIN){
checkIfExist(currentChild, name);
}
}
return false;
}
I think it happens because of the recursion you do:
if(currentChild.type == ProjectItemType.BIN){
checkIfExist(currentChild, name);
}
If this one gets kicked off before you can return true, you will start to run the function for a second time.
Now the first run can return a true, while the second (or even 3th, or 4th, etc) can return false and thus creating a new one, while also finding it.
Also if possible try to use arr.find or arr.findIndex and check if the value is -1 (or not found). This will make your code shorter, cleaner and less open for errors :)
But this will not work for nested arrays. Then you need to make an other function to first make a flat copy that includes all nested array before you do the arr.find or arr.findIndex. Still think that is the better solution.
You can use this to make nested array into a flat one:
let arr1 = [1,2,3,[1,2,3,4, [2,3,4]]];
function flattenDeep(arr1) {
return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
}
flattenDeep(arr1);// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
I have the following object:
var oBadge = {
COMMENT_CREATED: {
FIRST_COMMENT_CREATED: {
code: "FIRST_COMMENT_CREATED",
src: "",
name: "Socializer",
text: "Create a comment for an idea",
condition: {
today: null,
over_all: 1
}
}
}
};
i need to check if a string i get (for example "FIRST_COMMENT_CREATED") is contained in the oBadge model. The object contains more elements - not only the comment_created element. Therefore i cannot define to check it there.
I found the following function to determine wether the element is contained within the object or not, but I also need the contained data, not only the statement whether it is contained or not.
oBadge.hasOwnProperty("FIRST_COMMENT_CREATED")
What i'm basically looking for is a way to skip the second hierachy level on my check - like:
if(oBadge.[<all>]["FIRST_COMMENT_CREATED"] !== undefined) {
// return data
}
There is no way to skip the hierarchy without looping through the object, you should use the for...in loop:
for (var prop in oBadge) {
if(oBadge[prop].hasOwnProperty("FIRST_COMMENT_CREATED")) {
// return data
}
}
Use a function and a loop (for-in):
function find(key, obj){
if(!obj.hasOwnProperty) return false; // no primitive data please!
if(obj.hasOwnProperty(key))
return obj[key];
else return false;
}
for(var key in oBadge){
if(find("FIRST_COMMENT_CREATED", key))
// code
}
I have a stream holding an array, each element of which has an id. I need to split this into a stream per id, which will complete when the source stream no longer carries the id.
E.g. input stream sequence with these three values
[{a:1}, {b:1}] [{a:2}, {b:2}, {c:1}] [{b:3}, {c:2}]
should return three streams
a -> 1 2 |
b -> 1 2 3
c -> 1 2
Where a has completed on the 3rd value, since its id is gone, and c has been created on the 2nd value, since its id has appeared.
I'm trying groupByUntil, a bit like
var input = foo.share();
var output = input.selectMany(function (s) {
return rx.Observable.fromArray(s);
}).groupByUntil(
function (s) { return s.keys()[0]; },
null,
function (g) { return input.filter(
function (s) { return !findkey(s, g.key); }
); }
)
So, group by the id, and dispose of the group when the input stream no longer has the id. This seems to work, but the two uses of input look odd to me, like there could a weird order dependency when using a single stream to control the input of the groupByUntil, and the disposal of the groups.
Is there a better way?
update
There is, indeed, a weird timing problem here. fromArray by default uses the currentThread scheduler, which will result in events from that array being interleaved with events from input. The dispose conditions on the group are then evaluated at the wrong time (before the groups from the previous input have been processed).
A possible workaround is to do fromArray(.., rx.Scheduler.immediate), which will keep the grouped events in sync with input.
yeah the only alternative I can think of is to manage the state yourself. I don't know that it is better though.
var d = Object.create(null);
var output = input
.flatMap(function (s) {
// end completed groups
Object
.keys(d)
.filter(function (k) { return !findKey(s, k); })
.forEach(function (k) {
d[k].onNext(1);
d[k].onCompleted();
delete d[k];
});
return Rx.Observable.fromArray(s);
})
.groupByUntil(
function (s) { return s.keys()[0]; },
null,
function (g) { return d[g.key] = new Rx.AsyncSubject(); });