Search code examples
javascriptarraysobjectsearch

Facing issue with filtering objects inside array objects


I have below the object.

{
categories: [
  {
    "category": {
      "categoryName": "categoryName1"
    },
    "newsletter": [
      {
        "name": "AAA",
        "description": "111"
      },
      {
        "name": "BBB",
        "description": "222"
      },
      {
        "name": "CCC",
        "description": "333"
      },
    ]
  },
  {
    "category": {
      "categoryName": "categoryName2"
    },
    "newsletter": [
      {
        "name": "DDD",
        "description": "444"
      },
      {
        "name": "EEE",
        "description": "555"
      }
    ]
  }
]
}

I'm trying to perform search operation between the category, name, and description.

I have below the logic.

this.selectedCategoryData = this.categorisedData.filter((item) => {
    const categoryMatch = item.category.categoryName.toLowerCase().includes(filterValueLowercase);
    const newslettersMatch = item.newsletters.some(newsletter =>
        return newsletter.name.toLowerCase().includes(filterValueLowercase) ||
    newsletter.description.toLowerCase().includes(filterValueLowercase);
    );
    return categoryMatch || newslettersMatch;
});

This is my current logic. But it's working with some, not OR. Currently, it returns the whole subarray when found anything. If the value is found in the sub-array, it returns the whole array. How can I filter with subArray and assign the value?

Expected output like below:

First Scenario

filterValue = "AAA" //returns the below object
categories: [
  {
    "category": {
      "categoryName": "categoryName1"
    }
    "newsletter": [
      {
        "name": "AAA",
        "description": "111"
      }
    ]
  }
];

Second Scenario:

filterValue = "categoryName1" //returns the below object
categories: [
  {
    "category": {
      "categoryName": "categoryName1"
    }
    "newsletter": [
      {
        "name": "AAA",
        "description": "111"
      },
      {
        "name": "BBB",
        "description": "222"
      },
      {
        "name": "CCC",
        "description": "333"
      },
    ]
  }
];

Solution

    1. Use Array::reduce() to compose the result array
    2. Use case-insensitive RegExp for search the string

    const result = items.reduce((regex => (r, item) => {
    
      if(regex.test(item.category.categoryName)){ // category match - reuse the item
        r.push(item);
      }else{ // check news letters
        const newsletter = item.newsletter.filter(e => regex.test(e.name) || regex.test(e.description));
        if(newsletter.length){
          r.push({...item, newsletter});
        }
      }
      return r;
    })(new RegExp('aaa', 'i')), []);
    
    console.log(result);
    <script>
    const items = [
      {
        "category": {
          "categoryName": "categoryName1"
        },
        "newsletter": [
          {
            "name": "AAA",
            "description": "111"
          },
          {
            "name": "BBB",
            "description": "222"
          },
          {
            "name": "CCC",
            "description": "333"
          },
        ]
      },
      {
        "category": {
          "categoryName": "categoryName2"
        },
        "newsletter": [
          {
            "name": "DDD",
            "description": "444"
          },
          {
            "name": "EEE",
            "description": "555"
          }
        ]
      }
    ];
    </script>