Search in a nested object

Have you ever need to create a little search function that works for a small scope such as in a table? Recently I needed to build a simple search function in a small project. The users will have a table of customers where they should be able to search by multiple fields in one search function. Here I will demonstrate the use of recursive function to achieve this feature.

First of all, this is an example of the object structure in my use case.

const data = [{
	id: 1,
	name: "Arina",
	books: [{
		publishedYear: 2009,
		title: "Jakarta Abu-abu"
	}, {
 		publishedYear: 2012,
 		title: "Tulus"
 	}]
}, {
	id: 2,
	name: "Nikke",
	books: [{
 		publishedYear: 2019,
 		title: "Malam-malam aku sendiri"
	}, {
 		publishedYear: 2021,
 		title: "Tanpa cintamu lagi"
 	}]
}]

In this example, I want to be able to search by name, title, and year published, and get author and all their books when the input search match with any of that object's fields. The solution would be:

const nestedSearch = (data, input) => {
  return Object.values(data).some((val) => {
    if (Array.isArray(val)) return val.some((nestedVal) => nestedSearch(nestedVal, input));
    else {
      return String(val).toLowerCase().includes(input);
    }
  });
}

const filtered = data.filter((item) => {
  return nestedSearch(item, "malam")
});

In the first function nestedSearch, we test the value of the data against the input search. Object.values() is a function to return all the values of an object and present it in an iterable array. In this case, in the first call to nestedVal, Object.values(data) will return something like this:

[1,
	"Arina", 
	[{publishedYear: 2009, title: "Jakarta Abu-abu"}, 
	{publishedYear: 2012, title: "Tulus"}], 
...]

That is why, in line 3, we did a check if the value is an array. If a value is an array, we will do a recursive call to the nestedVal() until we get a value that is not an array so we can test against the input search. The function nestedSearch() will return a boolean which we will use to filter our data using Javascript filter.

The result of the function that we call above is an array containing object(s) that match with the text input:

const filtered = data.filter((item) => {
  return nestedSearch(item, "malam")
});
console.log(filtered);
// It will print the result like below
[
	{
		id: 2,
		name: "Nikke",
		books: [{
			publishedYear: 2019,
			title: "Malam-malam aku sendiri"
		}, {
			publishedYear: 2021,
			title: "Tanpa cintamu lagi"
		}]
	}
]

What do you think about this nested search? Do you think there is a more efficient way to solve this problem? Put your thoughts in the comment below!