Julie
Julie

Reputation: 514

filter a nested JSON object

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;

Upvotes: 2

Views: 3441

Answers (4)

Zachary Haber
Zachary Haber

Reputation: 11037

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);

Upvotes: 5

Werlious
Werlious

Reputation: 594

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);
};

Upvotes: 1

SARAN SURYA
SARAN SURYA

Reputation: 554

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]

Upvotes: 1

Fiury
Fiury

Reputation: 6348

I made an example on how could you filter the json using a input text

Example Filter Json

Take a look at the use of states and how to access the companies

Upvotes: 1

Related Questions