Personalized path-finding tree iterator

I am trying to implement a custom iterator in JavaScript that can traverse a DOM tree based on specific criteria provided by a callback function. The goal is to return an array of the nodes that match the criteria as the generator iterates through the tree structure.

  root
  / \
P1   P2
 |   |
T1   T2

The idea is to use iter.next(isP) or iter.next(isText) to update the matcher and move to the next matching node in the tree.

type Matcher = (node: INode) => boolean
export function* nextNode(node: INode, matcher: Matcher = () => true, store: []): Generator<INode, any, Matcher> {
  let reset: Matcher = matcher
  store = store.concat(node)
  if (reset(node)) {
    reset = yield store
    store = []
  }
  if (node.children) {
    for (let childNode of node.children) {
      yield *nextNode(childNode, matcher, store)
    }
  }
  return
}

One issue I have encountered is that the reset variable gets lost as the function call stack is popped. This causes problems when trying to switch the matcher in the middle of traversal, such as going from one text node to another. How can this be addressed?

const iter = nextNode(root, isT)
iter.next() <-- currently at T1
iter.next(isP) <-- expected to go to T2 instead of P2

Answer №1

Utilize the return value of the generator to carry forward the traversal state. Once yield* returns from the first child of the root, it will provide you with the store and matcher that were made available in the subsequent next call after yielding root and p1.

…
if (node.children) {
  for (let childNode of node.children) {
    [reset, store] = yield* nextNode(childNode, reset, store)
//  ^^^^^^^^^^^^^^^^^                           ^^^^^
  }
}
return [reset, store]
//     ^^^^^^^^^^^^^^

An illustration:

function* nextNode(node, matcher = () => true, store = []) {
  store.push(node.name)
  if (matcher(node)) {
    matcher = yield store
    store = []
  }
  if (node.children) {
    for (let childNode of node.children) {
      [matcher, store] = yield* nextNode(childNode, matcher, store)
    }
  }
  return [matcher, store]
}

const node = (name, children) => ({name, children})
const is = c => n => n.name[0] == c

const iter = nextNode(node("root", [
  node("p1", [node("t1")]),
  node("p2", [node("t2")])
]), is("t"))
console.log("until next t:", iter.next())
console.log("until next p:", iter.next(is("p")))
console.log("until next p:", iter.next(is("p")))

Answer №2

It appears that implementing a global matcher could be a straightforward solution.

type Matcher = (node: INode) => boolean
type TYield = INode[]
function* traverseNodes(
  node: INode,
  matcher: Matcher = () => true,
): Generator<TYield, TYield, Matcher> {
  let store: INode[] = []
  let reset = matcher
  function* _traverseNodes(node: INode): Generator<TYield, any, Matcher> {
    store.push(node)
    if (reset(node)) {
      reset = yield store
      if (!reset) reset = matcher
      store = []
    }
    if (node.children) {
      for (const childNode of node.children) {
        yield* _traverseNodes(childNode)
      }
    }
    return
  }
  yield* _traverseNodes(node)
  return store
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to run a function within an if statement without duplicating code if the condition is false?

When working with relay mutation code, I find it necessary to reload the store in order for it to sync with the database. This is because if the text being added is the same as previously added text, the relay store throws an error called flattenChildren.. ...

Is there a way to efficiently import only a specific data array into a NextJs page without importing the entire component dynamically?

Hey there, I recently went through a tutorial that explained dynamic importing in Next.js (https://nextjs.org/docs/advanced-features/dynamic-import) and it worked perfectly for components. Now, I'm facing a situation where I need to fetch data dynami ...

Creating a menu header similar to Gmail's design is a great way to

How can I create a fixed menu similar to the one in Gmail? I've attempted using CSS, but the div remains centered instead of sliding up on scroll like the Gmail menu. View in full-size image I've experimented with CSS properties, here's an ...

The implementation of useState is not functioning properly when used within a parent useState function

I am currently working with a Ticket child class where I set the total amount after changing the number of tickets. The issue I am encountering is that the setNumber function doesn't seem to work properly unless the setTotal function is commented out. ...

Attempting to nest an AJAX request within the success callback of another AJAX request

I am attempting to execute two jQuery ajax calls, with the second call being made from the success callback of the first. I have experimented with different variations of the code, such as adjusting the brackets. Below is my attempted code snippet: $.aja ...

Ensuring session data is updated when saving a mongoose model

I am facing a challenge with updating express session data in the mongoose save callback. Is there a way to access and modify the session data from within line 4 of the code? app.get('/', function(req, res){ var newModel = new Model(somedata); ...

Encountering issues with React Apollo hooks after integrating React Native into the monorepo system

I am in the process of setting up a React web app and a React-native app within a monorepo using yarn workspaces. The web and controllers are functioning properly, allowing me to successfully execute graphql queries to my apollo-express server. However, up ...

How can I set an object as a variable in JavaScript (specifically React) based on two different conditions?

const router = useRouter(); const { locale } = router; const featureId = props.id; let featureContent; featureContent = locale === "en" ? featureContentEn : locale === "de" ? featureContentDe : lo ...

The Intersection Observer fails to function properly with images

I recently discovered that images require different intersection observer code than text in order to function properly. After making the necessary changes to the code, my intersection observer is behaving quite oddly. As I scroll down, the image stays hidd ...

The process of incorporating user properties into the output of a Service Bus topic from a Javascript Azure Function

I'm currently developing a TypeScript Azure Function that utilizes an Azure Service Bus topic as its output. Although I am able to send messages successfully, I have encountered difficulties in setting custom metadata properties for the message. In m ...

Is it feasible to bring in a Typescript file into an active ts-node REPL session?

I want to experiment with some Typescript code that I have written. Currently, I usually run ts-node my-file-name.ts to test it out. But I am interested in making this process more interactive, similar to the Python REPL where you can import modules and ...

Leverage the power of Angular's $http module in conjunction with Django's urlpatterns to fetch

I am attempting to send a $http GET request to a Django URL pattern in order to retrieve a .json file. Is it possible to use urlpatterns to return a file instead of a view? Is this scenario achievable, or are there limitations preventing this from working ...

Is there a way to substitute the HOC with a single call and solely modify the prop?

One issue I've encountered in my project is the repetitive use of a Higher Order Component (HOC) for the header. Each time it's used, the props are set to determine whether header links should be displayed or not. My objective is to streamline th ...

Efficient method for managing complex JSON object updates using setState in React

My task involves handling structured data in JSON format, which I am unable to modify due to API restrictions. The challenge is to update the JSON file based on user modifications. { "id": 1269, "name": "Fet", &quo ...

Using typescript, we can pass a generic class as a parameter to a function

Currently, I am faced with the challenge of passing a class reference as a function parameter, and then calling a static method on this particular class. Additionally, there is a possibility that in the future, I may need to instantiate the class and inclu ...

Problems that seem to loop endlessly

I was working on some algorithm challenges from a coding platform, and I hit a roadblock with the "The Final Countdown" challenge. Here's what the challenge required: Provide 4 parameters (param1, param2, param3, param4), print the multiples of para ...

The next-auth/discord callbacks do not make any changes to the data

Currently, I am utilizing the next-auth/discord and facing an issue with the session callback not setting the user id to the session property as expected. [...nextauth].js import NextAuth from "next-auth/next"; import DiscordProvider from " ...

The combination of Object.keys() and the find function

Having trouble figuring out why I'm getting an error when attempting to use ES6 .find on the following data in order to retrieve the record with id number 3. { {id:10,title:'Dairy & Eggs'} {id:7,title:'Laundry & Household'} {id ...

Vue element fails to appear on the screen

As a newcomer to Vue.js and web development in general, I decided to dive into the vuejs guide. Something puzzled me - when creating a vue component using Vue.component(NameOfComponent, {...}) and inserting it into my HTML as <NameOfComponent></ ...

"Using axios and async/await in VUE.JS to perform multiple asynchronous GET

Perhaps this is a somewhat basic inquiry, as I am still learning the ropes of vue.js and javascript. Please bear with me if my question is not well-articulated or if the solution is straightforward... I am facing an issue where I need to retrieve data from ...