2

I have a search bar where you type employee name and it should return the name based on a filter. I have a nested JSON object (as shown below) where I need to drill into the object to access the employee's names in the array.

You can see the multiple options I tried to implement (they are commented out) My problem is the code is not filtering the names and returning all the names not the names searched for. I get this error TypeError: Cannot read property 'filter' of undefined

The following code works to access the employee names in another component:

{test.map((result) => (result.details.map((innerArr) => <h5>{innerArr.employee}</h5> )))} 

how can I implement the above in the below code

 const SearchByEmpComp = () => { const [company, setCompany] = useState([ { "company": "HIJ", "_id": "610aeaec618ac5902c466541", "details": [ { "employee": "Lesley Peden", "notes": "Lesley's note", "_id": "610aeaec618ac5902c466542" }, { "employee": "Wayne Smith", "notes": "Wayne's note", "_id": "610aeaec618ac5902c466543" } ], }, { "company": "ABC", "_id": "61003ff8e7684b709cf10da6", "details": [ { "employee": "David Barton", "notes": "some note!!", "_id": "610aebb2618ac5902c46654e" } ], } ]); //below code does not work //attemp 1 const test = company.filter((r) => r.details.map((innerArr) => { return innerArr.employee.toLowerCase().includes (searchField.toLowerCase()); }) ); //attemp 1 // const test = company.map((el) => { // return {...element, detail: element.detail.filter((details) => // details.employee.toLowerCase().includes // (searchField.toLowerCase()))} // }) //attemp 2 // const test = company.filter((res) => { // return res.details.map((innerArr) => // innerArr.employee.toLowerCase().includes // (searchField.toLowerCase())); // }); //attemp 3 // const test = company.filter((comp) => // comp.details.employee.toLowerCase().includes(searchField.toLowerCase()) // ); const deatils = () => { if (searchShow) return <EmpDetailsByName test={test} /> } }; return ( <> <FormControl type="search" placeholder="Type Customer Name Here" /> <div> <Button onClick={handleClick} > Enter </Button> <div>{deatils()}</div> </div ); }; 

code to render names

 const EmpDetailsByName = ({ test }) => { return ( <> {test.map((result) => (result.details.map((innerArr) => <h5>{innerArr.employee}</h5> )))} </> ); }; export default EmpDetailsByName; 
0

3 Answers 3

5

In addition to Werlious's answer, if you are looking for the companies to still be included, then you can map as shown here. The first mapping will still return companies where all the employees have been filtered out. The second mapping will filter out companies without any details.

The third is a more modern approach to returning only the employees. But there are countless variations to use for that.

const company = [ { company: "HIJ", _id: "610aeaec618ac5902c466541", details: [ { employee: "Lesley Peden", notes: "Lesley's note", _id: "610aeaec618ac5902c466542", }, { employee: "Wayne Smith", notes: "Wayne's note", _id: "610aeaec618ac5902c466543", }, ], }, { company: "ABC", _id: "61003ff8e7684b709cf10da6", details: [ { employee: "Lesley Peden", notes: "some note!!", _id: "610aebb2618ac5902c46654e", }, ], }, ]; const searchField = "les"; //attemp 1 const test = company.map((element) => { return { ...element, details: element.details.filter((details) => details.employee.toLowerCase().includes(searchField.toLowerCase()) ), }; }); console.log("test", test); const test2 = company .map((company) => { let details = company.details.filter((detail) => detail.employee.toLowerCase().includes(searchField.toLowerCase()) ); if (!details.length) { return null; } return { ...company, details }; }) .filter(Boolean); console.log("test2", test2); // Modern browser version of filtering to only the employees :) const test3 = company.flatMap((company) => company.details.filter((detail) => detail.employee.toLowerCase().includes(searchField.toLowerCase()) ) ); console.log("test3", test3);

Sign up to request clarification or add additional context in comments.

5 Comments

Haha +1 for beating me to that extra info! I had just added it in mine!
@Julie ... 1/2 ... How does such an output format help/support one in rendering a list of employee names from? The above result just repeats the structure of the original data source. A helpful target format was a list of items based on an employee's name. Thus for rendering the result list one just needs to iterate each item and print its name property value.
@Julie ... 2/2 ... In addition what happens if there are more than just one Jane Smith or John Doe? There can be various same names all of them of cause working for a different company. How do you handle such a case from the above result list? Maybe you consider again an approach that was presented and explained in detail to you from a former Q of yours ... Filter nested JSON objects with an array of objects
@PeterSeliger the first test Zachary did works beautifully. He maps the company and takes that result "element", expanding the iterables into individual elements, using the ...spread operator. This now allows access to the details array to be filtered when a user searches for the employee names. Thank you for pointing out the multiple name challenge, I have edited the solution code to reflect the same name twice within two companies (HIJ and ABC) and the result, when the code snippet is run, returns both companies with the same name "Lesley" it also works on my end in my code.
@Julie ... Good to know, that finally everything worked out well for you. Don't mind being your sparring partner once again.
1

So I have a simple solution using Regex,

employeeArray = [ { company: "HIJ", _id: "610aeaec618ac5902c466541", details: [ { employee: "Lesley Peden", notes: "Lesley's note", _id: "610aeaec618ac5902c466542", }, { employee: "Wayne Smith", notes: "Wayne's note", _id: "610aeaec618ac5902c466543", }, ], }, { company: "ABC", _id: "61003ff8e7684b709cf10da6", details: [ { employee: "David Barton", notes: "some note!!", _id: "610aebb2618ac5902c46654e", }, ], }, ]; // Set the state of the search string using state let searchUser = "Les"; // converting the search string to regex let convertedName = new RegExp(`.*${searchUser}.*`); searchResults = employeeArray .map((element) => { return { ...element, details: element.details.filter((employee) => { // Filtering based on the Regex return convertedName.test(employee.employee); }), }; }) // filtering based on the length of the data array length .filter((element) => element.details.length > 0); console.log(searchResults); 

Explanation : So as per your condition,

  • we take the user input first, and then convert them to a regular expression, so that we get all the recommendations of the name.
  • Filter, So for the Array.map, we first apply a high level map to the Array directly, an then we focus on the inner details array and apply a filter for that also, And I have made that return a boolean based on the presence of the data,
  • So what we do is MAP-> [OBJECTS -> [FILTER[DETAILS]]], So, now what will happen is that we filter each details array inside every JSON, and then we filter them based on the length of the details array so, we finally get the complete object out. The result will be as expected.

OUTPUT FOR SEARCHSTRING SET TO "LES"

[ { company: 'HIJ', _id: '610aeaec618ac5902c466541', details: [ { employee: 'Lesley Peden', notes: "Lesley's note", _id: '610aeaec618ac5902c466542' } ] } ] 

This will help you bring out all he suggestions for a given name. An Array[Objects]

Comments

1

I don't know how your filter is applied (what is it even filtering? Companies? Ids?), but really the search function should be its own little snippet as comments have suggested.

function SearchJsonForName(json,name) { let result = [] for(company of json) { for(employee of company.details) { if(employee.name.match(name)) result.push(employee.name); } } return result } 

That should get you started. If you need to apply a filter (maybe filtering companies?), you should apply it before you search the array (Or modify the function to take the filter as well ;>).

For a version that modifies the array in place:

function FilterByName(json,name) { return json.map(company => { let result = company.details.filter(employee => employee.name.match(name))); return result.length > 0 ? {...company,details:result} : false; }).filter(good => good); }; 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.