0

I need to implement a search query on a nested array of data. The user can input multiple search strings separated by a comma. I am storing those comma-separated strings as an array of strings and passing it to my filter function. The array of strings entered every time needs to match to 1 or more names of the subCategory array of objects.

The input string can be increased and the new property isActive:true can be attached to the object to which that string belongs, keeping the other unmatched objects with isActive:false added to them.

This is how my implementation goes so far. This implementation works fine in case I want to filter out only matched results.

    const filterData = (
            arrayOfData: Array<Object>,
            arrayOfString: Array<string>,
          ): Array<Object> =>
            arrayOfString &&
            arrayOfString.reduce((acc, current) => {
              const filteredData =
                arrayOfData &&
                arrayOfData
                  .map((category) => ({
                    ...category,
                    subCategory: category.subCategory.filter((data) =>
                      data.name
                        .toLowerCase()
                        .includes(current.toLowerCase()),
                    ),
                  }))
                  .map((data) => ({
                    ...data,
                    isActive: data.subCategory.length > 0
                  }));
          
              acc.push(...filteredData);
          
              return acc;
            }, []);
          
          export default filterData;

This seems to work fine partially. The acc.push(...filteredData) adds a new array every time a new string is added to the search, therefore increasing the array size a lot.

I am stuck on resolving this for quite a long, if anyone can help to implement this properly or in a better approach, much appreciated.

program_bumble_bee
  • 439
  • 2
  • 25
  • 57
  • 1
    Why the object with `id: 2,name: 'string2'` will come in filtered result when searched string is `string1` – brk Aug 02 '21 at 09:01

2 Answers2

1

That's how I would write it:

// let's add interfaces to simplify coding
export interface ICategory {
    category: string;
    name: string;
    subCategory: { id: number; name: string; }[];
}
export interface ICategoryWithIsActive {
    category: string;
    name: string;
    subCategory: { id: number; name: string; isActive: boolean; }[];
}

export function addIsActive(input: ICategory[], search: string[]): ICategoryWithIsActive[] {
    // do cleanup one time and not for each row
    const cleanSearchStrings = search.map(str => str.toLowerCase());
    return input.map(c => ({
        ...c,
        subCategory: c.subCategory.map(s => ({
            ...s,
            // true if seach terms array includes name
            isActive: cleanSearchStrings.includes(s.name.toLowerCase())
        })),
    }));
}

I'd suggest to give variables a real name like categories, the IDE will show you the type (array of strings).

Christoph Lütjen
  • 5,403
  • 2
  • 24
  • 33
  • Suppose I have a string like 'Sun Flower' with space (assuming I have one subcategory with the name 'Sun Flower'). Currently, If I enter the full string 'Sun Flower' it returns the result but not if I insert only 'Sun'. What If I type only the 'Sun' string and it should match this substring also and return the complete result as it is now. – program_bumble_bee Aug 02 '21 at 11:12
  • 1
    Take a look at https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/some to adjust the condition when isActive is true. E.g. `cleanSearchStrings.some(str => s.name.toLowerCase().startsWith(str))` – Christoph Lütjen Aug 02 '21 at 11:35
1

Why use reduce when you can achieve this with map and includes. Here's the implementation of your filterData function, which will return your desired result.

const filterData = (arrayOfData, arrayOfString) => {
  return arrayOfData.map((cat) => ({
    ...cat,
    subCategory: cat.subCategory.map((subCat) => ({
      ...subCat,
      isActive: arrayOfString.includes(subCat.name),
    })),
  }));
};
Ammar
  • 264
  • 1
  • 7