skip to Main Content

I have this array:

[
  {
    "label": "Group A",
    "fields": [
      {
        "value": "color1",
        "name": "Mike"
      },
      {
        "value": "color2",
        "name": "Mark"
      }
    ]
  },
  {
    "label": "Group B",
    "fields": [
      {
        "value": "red",
        "name": "Steve"
      },
      {
        "value": "priority",
        "name": "Laura"
      }
    ]
  },
  {
    "label": "Group C",
    "fields": [
      {
        "value": "red",
        "name": "Paul"
      },
      {
        "value": "priority",
        "name": "Mike"
      }
    ]
  }
]

and I’m using an input field to filter people by their name. So far I can only filter by "Group" by doing this:

post.label.includes(e.target.value)

but I’m having a hard time filtering by people’s names. The filtering is happening inside SearchBar component

For example, if the user enters "Mike" I should only see the groups with that user. Like this:

Group A
 Mike
Group C
 Mike

Here’s a LIVE DEMO. Thanks a lot in advance!

const handleSearchChange = (e: any) => {
        if (!e.target.value) return setSearchResults(posts)

         const resultsArray = posts?.filter((post: any)=> {
                // return post.label.includes(e.target.value)
               const result = post.fields.filter((field: any, idx: any) => {
                   return field.name.includes(e.target.value)
                })
                return result
            
            })
        setSearchResults(resultsArray)
    }

3

Answers


  1. You will need to loop through each fields inside the post array to find people with their name, here is a naive implementation:

    function searchByName(name, data) {
      const results = [];
    
      data.forEach(({ fields }) => {
        fields.forEach(({ name: fieldName }) => {
          if (fieldName.toLowerCase().includes(name.toLowerCase())) {
            results.push({ fields });
          }
        });
      });
    
      return results;
    }
    
    

    and your handleSearchChange function should be like this

    const handleInputChange = (e: any) => {
        const { value } = e.target;
        setSearchTerm(value);
        setSearchResults(searchByName(value, data));
      };
    
    
    Login or Signup to reply.
  2. I have taken a look at the live demo and seen the issue. Here are the things to change in order to achieve what you want.

    First create an hanldeChange function in App.tsx which would be passed to the SearchBar component. The content of this would be

    const [value, setValue] = useState("");
    
    const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
      setValue(e.target.value)
    };
    
    // Search the group anytime value changes
    // I advice you debounce if this is making and api call so it doesn't send a request for each alphabet the user types
    useEffect(() => {
      if (value) {
        const searchResults = (someJson as Fields[])
          .map((group) => ({
            ...group,
            fields: group.fields.filter(
              (field) => field.name.toLowerCase().match(value.toLowerCase())
            )
          }))
          .filter((group) => group.fields.length > 0);
        setSearchResults(searchResults);
      } else {
        setSearchResults(someJson);
      }
    }, [value]);
    

    This would be passed to the SearchBar component

    <SearchBar handleChange={handleChange} />
    

    Note that post and setSearchResults are no longer needed.

    Refactor the SearchBar component to look like this

    const SearchBar = ({ handleChange }: any) => {
      return (
        <header>
          <form
            onSubmit={(e) => {
              e.preventDefault();
            }}
          >
            <input type="text" id="search" onChange={handleChange} />
          </form>
        </header>
      );
    };
    

    Everything should be fine after you do this. Note that there are cleaner ways of achieving this and this solution is based on the demo sample.

    Login or Signup to reply.
  3. I think this is what you looking for.
    We need to process each group and its people’s names individually. So, instead of using filter, we use map to iterate through each group. Then we filter the people’s Names within each group.

    const handleSearchChange = (e:any) => {
        if (!e.target.value) return setSearchResults(posts);
    
        const resultsArray = posts?.map((post:any) => {
            const matchingFields = post.fields.filter((field:any) => {
                return field.name.includes(e.target.value);
            });
    
            if (matchingFields.length > 0) {
                return {
                    label: post.label,
                    fields: matchingFields,
                };
            } else {
                return null;
            }
        }).filter(Boolean);
    
        setSearchResults(resultsArray);
    };
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search