Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

walkUp function #20

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/index-fn.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export {default as map} from "./map.js";
export {default as walk} from "./walk.js";
export {default as transform} from "./transform.js";
export {default as reduce} from "./reduce.js";
export {default as walkUp} from "./walkUp.js";
19 changes: 19 additions & 0 deletions src/walkUp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getParent } from "./parents.js";
import { assertParentPointers } from "./util.js";

/**
* Walk up the tree from the given node, calling the callback on each node.
* @param {object} node
* @param {function} callback
* @returns {any} The first non-undefined value returned by the callback, otherwise undefined
*/
export default function walkUp (node, callback) {
while (node) {
const ret = callback(node);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently even in a tree with zero parent pointers you get the callback executed once. Is that what's best for the use cases that use this? Not a rhetorical question, I don't know. We need to look at the code using this function to see what would work well for these use cases.

Copy link
Contributor Author

@adamjanicki2 adamjanicki2 Apr 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main issue I see is the fact that we could execute an arbitrary number of callbacks before running into an error in the case where some ancestor node n nodes away doesn't have parent pointers set. The best option might even be to walk up the entire tree before executing any callbacks, and asserting parent pointers, and then after completing the first walk up, doing a second pass where you execute the callback. But of course, this also has a performance increase factor of 2, but might be worth it! Of course, if the callback didn't do any sort of mutation, then it would be safe to call before discovering an error, but I don't think we can count on that. I'd probably lean toward the above idea (walking up twice). What do you think @LeaVerou?

if (ret !== undefined) {
return ret;
}
assertParentPointers.call(this, node, "Cannot walk up the tree from a node with no parent pointer.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we print something more useful? Since we check on every node, it would help debugging to know which node actually failed.

node = getParent.call(this, node);
}
}
26 changes: 26 additions & 0 deletions test/walkUp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import walkUp from "../src/walkUp.js";
import trees from "./utils/trees.js";
import updateParents from "../src/updateParents.js";
import copy from "./utils/copy.js";

const tree = copy(trees[0]);
updateParents(tree);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to have some throws tests too, for when we don't have parents. Or should that just be handled in the parents tests?


export default {
name: "walkUp()",
run (node, cb) {
return walkUp(node, cb);
},
tests: [
{
name: "No callback",
args: [tree.right, () => {}],
expect: undefined
},
{
name: "Callback returns a value",
args: [tree.right, n => n.name === "1" ? n : undefined],
expect: tree
}
]
};