Filtering nested JSON arrays with the help of Knockout JS

I have been experimenting with filtering a nested JSON array using Knockout js. Let me share how my JSON data is structured:

{
"groups": [{
    "name": "Category1",
    "items": [{
        "question": "Question1",
        "answer": "Answer1"
    }]
}, {
    "name": "Category2",
    "items": [{
        "question": "Question2",
        "answer": "Answer2"
    }, {
        "question": "Question3",
        "answer": "Answer3"
    }]
  }]
}

The goal here is to filter based on the "answer" field. This is how my current HTML page is formatted:

 <div >
<div class="groups" data-bind="{foreach: {data: filteredGroups, as: 'group'}}">
  <div class="name-row" data-bind="text: group.name"></div>
  <div class="items" data-bind="{foreach: {data: group.items, as: 'item'}}">
    <div class="item-row">
      <div class="question-row">
        <div class="question-content">
          <div class="letter">Question</div>
          <div data-bind="text: item.question"></div>
        </div>
        <div class="notch">&nbsp;</div>
      </div>
      <div class="answer-row">
        <div class="letter">Answer</div>
        <div data-bind="text: item.answer"></div>
      </div>
    </div>
  </div>
</div>

Below you will find the logic that I am currently implementing:

 self.filteredGroups = ko.computed(function() {
  if (!self.query()) {
    return self.groups();
  }
  else {  

       var matchCount = 0;

      return ko.utils.arrayFilter(self.groups(), function(group) {
        return ko.utils.arrayFilter(group.items, function(item) { 
           //console.log("Entered==>" + self.query().toLowerCase() +  "  "  + "Search==>" + item.question.toLowerCase());
           var found = item.answer.toLowerCase().indexOf(self.query().toLowerCase());
           if(found >= 0){
             console.log("Number of occurrences" +matchCount++);
             return true;
           }else{
             return false;
           }
         });
      });

   }
});

However, I seem to be facing issues when trying to execute this logic as it does not provide the desired filtered results. Can anyone help identify what might be missing in my approach?

Answer №1

It seems like you are trying to filter items while maintaining their respective groups. Your current implementation of arrayFilter always returns an array of filtered items. However, since any array (even an empty one) will evaluate to true, the outer arrayFilter ends up returning all original group objects with all original items. This happens because the filtered items are lost due to implicit type conversion.

To make your computed function work properly, you can follow this approach:

self.filteredGroups = ko.computed(function() {
    if (!self.query()) {
        return self.groups();
    } else {

        var result = [];

        // Loop through each group
        ko.utils.arrayForEach(self.groups(), function(group){

            // Find items that match the query
             var items = ko.utils.arrayFilter(group.items, function(item){
                return item.answer.toLowerCase().indexOf(self.query()) >= 0;
            }); console.log(items);

            // If matching items are found, create a new group object with filtered items and push it to 'result'
            if (items.length > 0) result.push({
                name: group.name,
                items: items
            });

        });

        return result;
    }
});

You can view a working example on JSFiddle

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

Delay loading all images on the page except for the first one until the page is completely loaded

My website has a slider feature that displays images in a sequence, but currently all the images load at once and then get hidden by a script after the page is fully loaded. I tried using CSS to set a fixed height for the slider, which worked on desktop bu ...

Having an issue with fastify-multer where request.files is coming back as undefined during testing with Postman

In the process of developing an API that consumes multipart/form-data using fastify, I've integrated the fastify-multer plugin to handle file uploads for passing them to another third-party API. Currently, I'm testing with Postman, but encountere ...

Encountering an issue when trying to run the jQuery $.getJSON() function

I'm currently working on a jQuery Plugin that is designed to retrieve data from a JSON response. For some reason, the success method is not being called. Instead, it goes to the .error() method. Can someone please assist me with this issue? http://w ...

Is there a way to determine if a user is actively reading a page using Javascript?

As I work on creating a webpage that utilizes AJAX polling to bring dynamic content into the view, I have come across a challenge. With the page JavaScript constantly downloading updated information and rendering it while users are engaged with other conte ...

Establishing the Time-to-Live (TTL) with Redis-OM and Node Object Mapping

Recently, I stumbled upon the exciting new Redis-OM Node Object Mapping feature. Although I have limited experience with Redis, I am eager to dive into it now. Currently, I have a basic function in place for creating rooms but I want these rooms to automa ...

Is the original object cloned or passed by reference when passing it to a child React component through props?

When passing an object to a child component through the components props in React, does the object get cloned or does it simply pass a reference to the original object? For instance, in my App.js, I am importing a JSON object called ENTRY_DATA. Then, I pa ...

How to set the initial speed of a jQuery fadein animation

I'm looking to optimize the loading process of this div on page load so that it displays instantly and then refreshes automatically every 10 seconds. Currently, it refreshes every 10 seconds but I would like it to load instantly as well. Is there a ...

Adjust the text color of a div element by clicking a button with the help of JavaScript

I am trying to change the font color of my div when a button is clicked using JavaScript. The two buttons I have are 'Green' and 'Red'. <button type="button" class="green">Green</button> <button type="button" class="red" ...

JSON to Table Conversion

On the hunt for a php script that can load CSV files? Look no further than this snippet: echo json_encode(file(csvfile.csv, FILE_IGNORE_NEW_LINES)); Check out the output: ["foo,bar,baz","foo, foo,bar","bla,bla,blubb"] Need to integrate this into your ...

dissecting mongo queries using nodes

I am thinking about organizing my mongo db queries into a separate js file to make it easier to reuse the code. I have tried the format below but it doesn't seem to work. Does anyone have any suggestions on how I could accomplish this? queries.js va ...

Decomposition of words in VueJS based on numerical values

Is there a way to handle word declension based on the number of items in VueJS? For example, displaying "0 skins", "1 skin", "2 skins", "3 skins", "4 skins", "5 skins", and so on. I currently have a basic code snippet with hardcoded text: <div class=&q ...

Transferring data from a JavaScript variable to PHP using AJAX

I’m currently working through the tutorial at http://www.w3schools.com/php/php_ajax_database.asp and I think I have a good grasp of how it all operates. This is my code: PHP: <?php include('./db.php'); $PM = mysqli_query($con, "SELECT DIS ...

What is the best way to invoke a controller method using jQuery within a cshtml file?

I am working on a project where I need to add user information to the database by calling a function in my controller class. The user's information is entered through a form created in a .cshtml file that interacts with an external JavaScript file. Is ...

Instructions for creating a function in TypeScript that accepts an array as user input and determining its length

Looking to develop a TypeScript function that can take an array of any type as input, calculate the number of elements present in it, and return the count. An example output would be: If user inputs: ["soccer", "basketball"] Then output: 2 Any suggestion ...

Issue with Material-ui autocomplete not updating its displayed value according to the value prop

My task involved creating multiple rows, each containing a searchable Autocomplete dropdown populated through an API, along with other fields. Everything was functioning perfectly until I encountered an issue when deleting a row from the middle. After dele ...

Issue with getStaticProps not updating fetched values in Next.js production environment

I am currently working on building a blog using Next.js. Since the back-end is taken care of by another team, I am utilizing fetch API calls in getStaticProps to retrieve my articles, even though it may be considered best practice to interact with the data ...

What is the method for invoking the aggregate function within a different function?

I'm struggling to pass the data from t1 function to t2 function. I attempted a method but it resulted in 'undefined'. Could someone please assist me with this? Thank you! function t1() { db.users.aggregate( [{ ...

Vue.js - encountering an issue with undefined response data

Greetings! I've encountered a script issue while trying to submit a form in VUE. The error message displayed in the developer panel states: "TypeError: error.response.data.errors is undefined". As a newcomer to Vue, I'm seeking some assistance as ...

Tips for sending an email without using MAILTO in JavaScript or jQuery

Today is a great day for many, but unfortunately not for me. I've been struggling with this issue for over two weeks now and can't seem to find a solution anywhere on the internet. Stack Overflow always comes to my rescue when things get really t ...

Embrace a variety of HTML template options when setting up a ReactJS component

Can someone help me understand how to render a component with template HTML data in ReactJS? class Options extends React.Component { render() { const othList = []; [1, 2, 3, 4, 5].forEach((t) => { othList.push(t); ...