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

Is there a way to set a personalized callback function when closing a modal with MicroModal?

I've been utilizing MicroModal for showcasing a modal window. Everything seems to be working smoothly, except for when I try to trigger an event upon closing the modal. It's unclear to me where exactly I should implement this callback function. ...

Unable to transfer an object into a component due to a subscribe restriction

I have encountered an issue: I am making a post request to save an object in the database. The request takes JSON input with the values of the object to be saved. After saving the object in the database, I need my servlet to return the saved object so that ...

Can you explain the distinction between using this.function and making a function call in React?

I'm new to React and recently came across some code in a project that confused me. Could someone please clarify the distinction between this.function and the following function call used in a React event handling prop? <button onClick={this.clickH ...

PHP object representing a datetime in JSON format

How can I convert a JSON datetime object sent to JavaScript into a JavaScript datetime object? The PHP return is: "date": { "lastErrors": { "warning_count":0, "warnings":[], "error_count":0, "errors":[] }, "timezone": { "nam ...

What is the best way to distinguish if users are accessing my Facebook app through an iframe or by directly entering the

If my Twitter app url is http://tw.domain.com/ and the app url is http://apps.twitter.com/demoapp/ I need to ensure that users cannot access the application url directly, they must view it within an iframe on Twitter. What method can I use to detect & ...

Highlighting table column when input is selected

I am working with a table where each <td> contains an <input>. My goal is to apply the class .highlighted to all the column <td>s when an <input> is being focused on. Additionally, I want to remove this class from all other columns ...

AngularJS: Importing a parent directive within a child directive

Take a look at this Plunk example to understand better. I'm attempting to create a test scenario for accessing complex directives, but I encounter an error when trying to call a method from the parent directive: Parent Directive app.directive(&apos ...

Using CKEditor in an AngularJS web application: Tips and tricks

I'm having trouble integrating the ckeditor into an HTML page that's built with angularjs. Despite trying out numerous examples, such as the ng-ckeditor and ckeditor directives, I haven't found a solution that works for me. What I need is ...

Raspberry Pi 4: My LED is only blinking 8 times instead of the expected 16 times

I have encountered an issue with my program run, compilation, and result. In the screenshot below, you can see that my LED is only blinking 8 times instead of the anticipated 16 times. My expectation was for the LED to blink every 0.25 seconds for a total ...

Utilize or Bring in an external JavaScript file within Ionic 2

Currently working with Ionic 2 and Typescript Angular 2 and facing an issue. I need to utilize an external JavaScript file located at . How can I import or include this in my project? ...

What is the best way to refresh or render a list in a React application?

While attempting to update the calendar days by using useState, I encountered a "too many re-renders" error. Even though the list was updated correctly, the component did not render on the screen as expected. I am struggling with figuring out how to update ...

Discovering nested routes in Ember.js through search functionality

I am currently working on implementing a search functionality within a nested route that shares a model. Below is an example of my code: The "Products" and "Search" functionalities return a JSON response. Router Market.Router.map -> @resource &a ...

Converting HTML content into a single string simplifies data manipulation and extraction

exampleHTML=" This is sample HTML code that needs to be converted into a string using JavaScript

" I am looking to transform it into a single string format like str="This is sample HTML code, that needs to be converted into a string, usin ...

audio enhancement in a web-based game

I'm having trouble getting a sound effect to play consistently in my game whenever there is a hit. Sometimes the sound plays, other times it does not! Here is the code I am using: <script> var hitSound = new Audio(); function playEffectSound ...

ng grid shift select issue mistakenly selects extra rows

Take a look at my plunker here: link var app = angular.module('app', []); // encountering a silly error that prevents me from pasting the entire code, please refer to the plunker for details To replicate the issue: Click on the first row with ...

Having issues transferring the variable from JavaScript to PHP?

When attempting to pass a variable via AJAX request in JavaScript, I am encountering an issue where the variable is not being received in my PHP file. I'm unsure of where the problem lies. Can anyone help? JavaScript code var build = { m_count : ...

gulp-webpack is unable to locate node packages

Currently working on developing a modern Angular application. I have opted to use gulp-webpack for quick development builds. To handle my TypeScript bundling and node modules dependencies, I am relying on webpack. However, it seems that gulp-webpack is no ...

Please provide the text input for the specified version numbers (1.1, 1.2, 3.0, etc.)

I'm currently working on a form that includes a section for searching by version number: <label class="formLabel">Version</label> <html:text property="valueVersion" styleClass="value" tabindex="11"/> <br/& ...

Incoming information obtained via Websocket

Currently, I am working with Angular and attempting to retrieve data from the server using websockets. Despite successfully receiving the data from the server, I am faced with a challenge where instead of waiting for the server to send the data, it retur ...

A ServiceWorker used a promise in FetchEvent.respondWith() that resulted in a non-Response value of 'undefined'. This caused an issue in browser-sync

There are times when my server encounters an error in the console: Failed to load ‘http://localhost:3000/browser-sync/socket.io/?EIO=3&transport=polling&t=Lm2wn4p’. A ServiceWorker passed a promise to FetchEvent.respondWith() that reso ...