Adjust the tally of search results and modify the selection depending on the frequency of the user's searches within an array of objects

Seeking assistance with adding a new function that allows users to navigate to the next searched result. Big thanks to @ggorlen for aiding in the recursive search.

https://i.stack.imgur.com/OsZOh.png

I have a recursive search method that marks the first value as selected = true, and if it's within a nested array, sets showTree=true.

  • How can I implement a feature where clicking on the next search record will update the 'selected' value to the next result while removing the previous one?

  • Additionally, how can I reflect changes in the 'showTree' based on the updated results?

  • Is there a way to introduce a variable that updates each time the search function is called...

  • Providing a means for users to navigate back to the previous search result.

const expandPath = (nodes, targetLabel) => {
  for (const node of nodes) {
    if (node.label.includes(targetLabel)) {
      return (node.selected = true);
    } else if (expandPath(node.item, targetLabel)) {
      return (node.showTree = true);
    }
  }
};

// Output

expandPath(testData, 'ch');

//// add variable for count  example: 1 of 25

console.log(testData);

//if user click on nextrecord after search


//nextrecord(){
//logic to remove the selected true from current and add for next
//update showtree
//update recordNumber of totalValue example: 2 of 25
//}


//child3 should get selected true and remove child1 selected true and showtree
//same add showTree= true based on selected value

//if user click on previous record after search

//previousrecord(){
//logic to remove the selected true from current and add for previous
//update showtree
//update recordNumber of totalValue example: 1 of 25
//}



console.log(testData);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script>
// Test Data
const testData = [
  {
    id: 1,
    label: 'parent1',
    item: [
      {
        id: 21,
        label: 'child1',
        item: [
          {
            id: 211,
            label: 'child31',
            item: [
              {
                id: 2111,
                label: 'child2211',
                item: [{ id: 21111, label: 'child22111' }]
              }
            ]
          },
          { id: 222, label: 'child32' }
        ]
      },
      {
        id: 22,
        label: 'child2',
        item: [
          {
            id: 221,
            label: 'child421',
            item: [{ id: 2211, label: 'child2211' }]
          },
          { id: 222, label: 'child222' }
        ]
      }
    ]
  },
  {
    id: 2,
    label: 'parent2',
    item: [
      {
        id: 21,
        label: 'child2',
        item: [
          {
            id: 511,
            label: 'child51',
            item: [
              {
                id: 5111,
                label: 'child5211',
                item: [{ id: 51111, label: 'child52111' }]
              }
            ]
          },
          { id: 522, label: 'child352' }
        ]
      }
    ]
  }
];
</script>

Answer №1

To implement the code below, utilize the provided example:

    const userData = [
  {
    id: 1,
    name: 'user1',
    data: [
      {
        id: 11,
        title: 'post1',
        content: [
          {
            id: 111,
            message: 'comment1',
            replies: [
              {
                id: 1111,
                response: 'reply1',
                individual: [{ id: 11111, feedback: 'individual1' }]
              }
            ]
          },
          { id: 112, message: 'comment2' }
        ]
      },
      {
        id: 12,
        title: 'post2',
        content: [
          {
            id: 121,
            message: 'comment3',
            replies: [{ id: 1211, response: 'reply2' }]
          },
          { id: 122, message: 'comment4' }
        ]
      }
    ]
  },
  {
    id: 2,
    name: 'user2',
    data: [
      {
        id: 21,
        title: 'post3',
        content: [
          {
            id: 211,
            message: 'comment5',
            replies: [
              {
                id: 2111,
                response: 'reply3',
                individual: [{ id: 21111, feedback: 'individual2' }]
              }
            ]
          },
          { id: 212, message: 'comment6' }
        ]
      }
    ]
  }
];

// reorganize tree structure into an array and incorporate parent pointer
const flattenData = (userData) => {
  let flattenedData = [userData]
  if (userData.data) {
    for (const item of userData.data) {
      item.parent = userData;
      flattenedData = flattenedData.concat(flattenData(item));
    }
  }
  return flattenedData;
}

let flattenedUserData = [];

// transform user data into a flat structure
for (const user of userData) {
  flattenedUserData = flattenedUserData.concat(flattenData(user));
}

// update showInformation flag
const toggleView = (item, expand = true) => {
  const parentInfo = item.parent;
  if (parentInfo) {
    parentInfo.showInformation = expand;
    if (parentInfo.parent) {
      return toggleView(parentInfo, expand);
    }
    return parentInfo;
  }
  return item;
}

/**
 * 
 * @param {searchKeyword} lookup 
 * @returns method to navigate with forward or backward parameter
 */
const locateData = (lookup) => {
  let indexValue = -1;
  const itemsFound = flattenedUserData.filter(x => x.name.includes(lookup));
  return (forwardMove = true) => {
    if (indexValue > -1) {
      itemsFound[indexValue].viewed = false;
      toggleView(itemsFound[indexValue], false);
    }
    indexValue = indexValue + (forwardMove ? 1 : -1);
    let informationItem = null;
    if (indexValue > -1 && indexValue < itemsFound.length) {
      itemsFound[indexValue].viewed = true;
      informationItem = toggleView(itemsFound[indexValue], true);
    }
    return {
      informationItem,
      indexValue,
      totalMatches: itemsFound.length
    }
  }
}

const moveLocation = locateData('comment6');

// next information
let details = moveLocation();

// previous information
details = moveLocation(false);

// information output will be as follows
/**
 * {
 *  informationItem: root of current item with showInformation and viewed flags or null if out of range,
 *  indexValue: current match,
 *  totalMatches: total matches found
 * }
 * 
 */

Answer №2

If you focus on one task at a time, it becomes easier to achieve the 'next' functionality you desire by transforming your recursive search into a generator function:

function* expandPath(nodes, targetLabel) {
  for (const node of nodes) {
    if (node.label.includes(targetLabel)) {
      yield (node.selected = true);
    } else if (expandPath(node.item, targetLabel)) {
      yield (node.showTree = true);
    }
  }
};

const gen = expandPath(mynodes, "thisTargetLabel");
gen.next()
gen.next() //<-- the next one

Without more context, answering the other questions is challenging, but it appears that incorporating state using an (es6) class could be beneficial:

class Searcher {
  constructor(mynodes, mylabel) {
    this.count=0;
    this.nodes=mynodes;
    this.label=mylabel;
    this.generateMatches(this.nodes);
    this.selectNode(this.matches[0]); // select the first node
  }

  generateMatches(nodes){
    this.matches=[];
    for (const node of nodes) {
      if (node.label.includes(this.label)) {
        this.matches.push(node);
      } else {
        this.generateMatches(nodes.node) 
      }
    }
  }

  updateTreeById(id, node){
    this.nodes.forEach(n=>n.showTree = false);
    for (const node of this.nodes) {
      if (node.id === id) {
        //noop but we are here
      } else if(this.updateTreeById(id, this.nodes.node)) {
        node.showTree = true;
      }
    }
  }

  selectNode(i){
    const index =  i % this.matches.length;
    this.currNodeId =  this.matches[index].id;
    this.matches[index].selected = true // we are wrapping around
    this.count = i; // setting your current count
    this.updateTreeById(this.matches[index].id)
    // update logic, reset trees
  }

  nextNode(){
    this.selectNode(this.count + 1)
  }
  prevNode(){
    this.selectNode(this.count - 1)
  }
}


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

"Modifying Code Aesthetics in WebStorm/IDEA: A Step-by-

Here is the code style I am currently using in my JavaScript project: var obj = { a: 1 , b: 2 , c: 3 } var arr = [ 'a1' , 'a2' , 'a3' ] const w = 1 , w2 = 2 , w3 = 3 The team claims they are following npm's co ...

Having trouble copying an iframe from one div to another div?

I'm having trouble transferring an iframe to another div. Here is the code I am using: <script type="text/javascript"> $(document).ready(function () { setInterval(function () { if ($('#d1').html() != $('#d ...

Allow the words to seamlessly transition back and forth between columns in a continuous cycle

Currently, I am attempting to showcase a large text in a format similar to a book. Each "page" has designated width and height, with content displayed over two columns: left and right. In this layout, page 1 is on the left, page 2 on the right, page 3 on t ...

Ways to revert to the initial state in React.js

Check out my codeSandbox project here: https://codesandbox.io/s/bold-lederberg-d2h508?file=/src/Components/Products/Products.js I'm having trouble getting the items back to their original presentation when clicking on "default" - sorting by price wor ...

Can someone explain the process of utilizing forEach to add elements to an array using React's useState hook?

I'm attempting to replicate the functionality of the code snippet below in a React environment. Despite several attempts, I have not been able to achieve the same outcome. Old code that worked (non-React) const array = [] res = await getData() res. ...

What is the method for retrieving the IDs of checkboxes that have been selected?

I attempted running the following code snippet: <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="http://static.jstree.com/v.1. ...

Enhance your application with realtime listeners that provide updates without the need to retrieve the entire database each time changes

Can you clarify the meaning of this Firestore concept? In order to keep your app's data up-to-date without having to retrieve the entire database every time there is an update, you can add realtime listeners. By adding these listeners to your app, ...

What is the functionality of a nested child directive root element when placed at the same level as the parent directive root element?

Hello there, I'm currently facing a unique situation that has me stumped. Is it possible to replace the content of a directive's root element with that of a nested (child) directive? Here is an example scenario: <div id=”main”> <n ...

Deliver real-time updates directly to clients using Django Channels and Websockets

Currently, I am working on a page that needs to display live-updating data to the client. The rest of the website is constructed using Django framework, so my approach involves utilizing Channels for this purpose. The data that needs to be showcased is st ...

Displaying rows in a mat-table based on a certain condition

Is there a way to only display data in the table if the status is 'done'? I attempted to remove the status, but it still shows the row. Any suggestions on how to achieve this? data { equipmentOrdered: 'laptop', qty: 1, s ...

What is the best way to find the vertex positions of a JSON model in

Here is the code I am using to load a 3D model in THREE.js: var noisenormalmap = THREE.ImageUtils.loadTexture( "obj/jgd/noisenormalmap.png" ); noisenormalmap.wrapS = THREE.RepeatWrapping; noisenormalmap.wrapT = THREE.RepeatWrapping; noisenormalmap.repeat. ...

Is it possible to reposition the vertical scrollbar to a location that is not on the left or right side?

Whenever I resize my modal on small screens, a horizontal scrollbar appears, causing the vertical scrollbar to disappear as it gets stuck on the right side. I am looking for a solution to keep the vertical scrollbar on the right side of the modal while scr ...

What causes the immediate firing of the DOM callback in Angular 1.2.9?

Check out a live demo here View the source code on GitHub here Angular 1.2.9 brought in DOM callbacks with the introduction of $animate:before and $animate:after events triggered during animations. However, it seems that the $animate:after event is trigg ...

Retrieving the contents of a unique 404 error page using ajax

Currently attempting to retrieve the content of a custom 404 page through ajax (I need to extract a counter value from this page using Greasemonkey). Regrettably, jQuery's .fail method in ajax does not provide the option to access the page's con ...

Avoid loading the page when the browser's back button is pressed with vue-router

In my application, I have a "Home" page as well as a "Success" page. On the Success page, there is a button that, when clicked, redirects to a URL like https://google.com, using window.location.href='https://google.com'. Currently, I am able to ...

Creating a Mithril.js Single Page Application (SPA) using a JSON data source: A

I am currently working on creating a single page application (SPA) using Mithril.js. While I have come across some helpful tutorials like the one here and on the official Mithril homepage, I am struggling to combine the concepts from both sources effective ...

How to Extract Byte Array from ImageView in Android App

After successfully uploading an image from my app to the server and displaying it in an imageview, I encountered an error while attempting to re-upload the existing image from the imageview to the server. The error message displayed is as follows: ava.lan ...

Allow images to be uploaded using the browser-policy package in Meteor

Can anyone help me figure out how to use Sir Trevor JS in Meteor for uploading images without encountering this error message? When attempting to load the image 'blob:http%3A//localhost%3A3000/a28ef7dc-ee51-4290-9941-6b8fc317e685', I am receivin ...

Show only the items in bootstrap-vue b-table when a filter is actively applied

How can I set my bootstrap-vue b-table to only show items when a filter is applied by the user (i.e., entered a value into the input)? For example, if "filteredItems" doesn't exist, then display nothing? This is primarily to prevent rendering all rows ...

Exploring the capabilities of zooming on SVG elements using D3 within an Angular

I want to implement pan/zoom functionality on an SVG element. I came across a tutorial that suggested using d3.js for this purpose, you can find it here Below is the code I have tried: import { Component,AfterViewInit,OnInit } from '@angular/core&a ...