How To Create A Global "JSON Search Algorithm" In JavaScript.
Tags: javascript, webdev, algorithms
Published: 2021-03-01 10:24:34

Writing a **Search** feature for every single page or section on your website can be really tedious, and even if you create a reusable function, you might end up changing the code multiple times for it to work for new or dynamic types of JSON data.

I first noticed this problem at work, where I had to write and modify my reusable search function to work for new types of "JSON data" or situations where minor changes would require me to handle page-specific search instructions.


For the most part, You'd require to know the **keys** of the **JSON** you'll be working with, so let's say you want to implement a search that can... search using *First Name*, *Last Name*, *Email Address* etc your code might look something like this :


![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h9jl4mzcktkd51341loz.png)


If you read the code closely, you'd realize I knew beforehand the **keys** of the data I'll be working with, and let's presume in the future, the backend changes and **email** is taken out, or **phoneNumber** which in this case, the code will break (crash) or the backend includes a new data we'd love to also be able to search with, like **age** or **role**, which would result in us changing the code to cater for **age** etc.

You should already start seeing the problem here so... I decide to come up with a simple algorithm that'll eliminate all these problems by : 

* Making sure you don't even have to know or care about the keys
* Still works, even for changing or dynamic JSON data
* Includes combination settings e.g combining *first name*, *last name* and *middle name* in search inputs.
* Prevent your heart from racing when your company/friend(s) is/are testing your application
* Saves you a lot of time and burnout 😎

Okay, now let's get to it, we'll start from the basics then improve along the way.

##Version 1

First, the most basic requirement is your array of data and a function that'll feed on it, so if you have : 

![JOSN code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8b2wjzgyt3odp77zt23l.png)

We'll start the search code by :

* Declaring a search function that takes 3-arguments ( the **event** (in this case, its the user input), the **JSON array**, and the **function** (or variable) that'll receive the result.

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9cgo7oll6ujeayu33wvs.png)

After that, we'll fix the code below to prevent the page from refreshing and getting the user input (typed search value)

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nbhrxyx0xu3xjmaqdwi2.png)

The second step here would be to loop over the array and get the search result using [Array.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), and what this simply does is loop through the array and do some conditional checks that we'll write, and if the check passes, it'll return the specific data that passes the test. Check the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) for an in-depth information.

So we'll have something like :

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2rt5yn18ghidaycfmmmf.png)

Now, it's all starting to look a lot like the old code I complained about, but here comes the trick of this whole saga.

If you remember I said you won't have to worry about knowing the **Keys** of the JSON data? (`{"Key" : "value"}), well... we're in luck because there's a function for getting the **keys** (even values) of an object in JavaScript.

We'll be using the `Object.keys(yourJSONArray)` function, which returns an array of **Keys** we can loop over. Check the [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) for an in-depth explanation. Then we can perform a [forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) on the returned array (as you might have guessed), but in our case, `forEach` (or even a `for-loop`) is not an option as we'll be using [Array.some](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) and I'll explain why (because I think it's important to note), but first, take a look at the code at this stage.

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k0xa5h2kxv00tnldrmvo.png)

If you look through the code again, you'll notice that `Array.filter` returns the data that passes our conditional check, and we're doing the checks on the `Object.keys` but in this case, we want to **stop/terminate** the loop immediately we find a matching value. What I mean is, if you type **Dev** in your search field and the code is currently looping over the first data in the Object: 

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzgcvud6y3jzq0mnpz3o.png)

The checks we'll do here is computing if the current **Object Value**  e.g `{"key": "value", "key": "value"}` includes the search input, so if the user types **Dev** in the search box, it'll match **firstName** and **emailAddress** and if we have any other **value** with the word "**Dev**" it'll match that too, but here's the thing about `Array.some`, it'll stop looping once we find the first matching value (**firstName**) and that's why we're not using **forEach** because we'd have to manually write more code to make it work and *we lowkey can't stop a forEach loop* (let me know if am wrong). 


Let's move forward. The code and conditions we'll write in the **Array.some()** will : 

* **Get the current value** of the Object, and since we don't know the keys beforehand, we'll use `data[key]` which allows us to get the values of objects with dynamic keys (applies in our case).
* **Convert the value to a string** using [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify), and it'll look something like : `JSON.stringify(data[key])`
* **Convert the value to lower-case** because we've also converted the user input to lowercase `const input = event.target.value.toLowerCase();`

> **Note**: "A" is not equal to "a" and that's why we're comparing both the input and the value at a lower-case level

* **Trim out any trailing white spaces** using `.trim()`. This will eliminate white spaces at the start or end. For example, " name" or "name " is not equal to "name" and that's why we remove any trailing whitespace. And lastly
* Check if it includes the **search input value** using `.includes(inputValue)`
* pass the result to a function/variable

Let's take a look at the code to get more clear.

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z1e2riyty0rqwuds6ybu.png)

At this point, this code is almost ready to ship because right now it works but we can make it better.

## Version 2

Why version 2? I'll explain why, if you take a look at the data again, you'll notice that we have "firstName" and "lastname", we could even have "middleName" later in the future. But currently, our code cannot search in combinations of first-and-last-name, it can only match one at a time.

So... what if we could find a way to get around this? Well... there is a way, and we'll do that by including a fourth argument called "combinations" (or you could call it anything you want), so we'll have something like : 

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8wj1cam4dzhzf65pq1kn.png)

The code I wrote at work to combine searching with "firstName" and "lastname" looks like this : 

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wobhgf6ay3szbfog9r1y.png)

and you can see that it looks ugly, and I had to also know the keys of the object.

Here, we'll allow the user or caller of our function to decide what combinations they want and in what order they want it because think about it, what if they want it in the order: "lastName"-"firstName" or "middleName"-"firstName" or "firstName"-"lastName"-"middleName"? Exactly!

Our implementations will be slightly different and we'll begin by looping over the combinations passed by the caller or user of our function/algorithm.

In the `.filter()` function we'll fix in our combination logic

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fkekprrh5sqz783y04j8.png)

Then we'll loop over the combinations and add them in the order that the user of our algorithm wants.

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d13zl7nms1e6d2xi1ots.png)

Our current code altogether looks like this: 

![universal search algorithm code image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ias4boatd1za4l5ec3z.png)

Good, we made it this far, but... wait a min, something's not quite right about this code 🤔. What's wrong with it? Well... it's full of bugs at the moment, lol. And we'll fix them, but first, what are the obvious bugs am talking about? Here's a list

* Bug NO.1

First, I said we don't have to know the key's right? But then am giving the user of our algorithm the ability to provide **Keys** of the data they want to combine, and what's wrong with this? Well... what if they supplied a key that's not even existent in the JSON data? Bugs! We'll fix it.

In JavaScript, we can check if a **key** exists in an object by using [Object.hasOwnProperty("someKey")](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty). In other words, `data.hasOwnProperty("firstName")` will return `true` and `data.hasOwnProperty("non-existent-key")` will return `false`

Our improved code should look like this: 

![universal search algorithm code image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kijesbjbn2uzwxirbfrr.png)

Are we done? Nope! Am sorry but we ain't.

* Bug NO.2

let's take a moment to think about what would happen if the user never passes a value for **objectData** and **combinations**. There's going to be a problem because we're always trying to perform some actions on these arguments, and we can fix the code by providing a default value.

So if we forget to pass a value for these arguments, we'll still be fine, but how do we do that? We do that by providing an empty array as a default value: 

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pg5s8seh592fk8q53co4.png)

Great! We made it even further, are we done? Yea, we are, but... Maybe we ain't 😎.

There's one more thing I'd like to point out about this algorithm. In JSON data, sometimes, values can come back as **null**, **undefined**, **true** or **false**, you might have seen this a lot if you've consumed some backend data in the past or current.

So, if a user search the word **null**, **undefined**, **true** or **false** every object/data that has a the value **null** or **undefined** etc will show up.

At my workplace, I allowed the search of **false** and **true** but not **null** or **undefined** because the API I was working with mostly had an `{isActive: true}` or `{isActive: false}` key-values, which allows me to search users that are currently activated/deactivated.

> I just type "true" or "false" and that does the magic

## Version 3

My current code at the time of writing this blog is: 

![universal search algorithm code image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/83lg6rw560bnhf8lao79.png)

For someone that might want to use the code in a search input will do something like: 

![code snippet image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ey8vb60q31kzdm9c7w83.png)

And they won't have to worry about what changes about the student data in the future.
Plus even if you're consuming an API with random JSON data (Never seen this happen though) like say, data of persons, and robots, and cat names etc... you get the gist?

![random JSON data image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zrvk9ace2lcpjknl2d7u.png)

you won't have to worry, the search still stands.

The only new thing in my previous snippet (Version 3) is the `&&` (and) `||` operator and what `&&` simply means is, execute the code on the right if the code to the left (before the &&) is true.
`||` simply means, do what's on the left if the condition is true **Or** do what's on the right if what's on the left is false. I don't feel I should talk much about these operators, if you're not familiar with it, you can look them up, it's no big deal but... it really does save you a lot of typing.

## Conclusion 

There's no denying the fact that every code can be made better, and if you ever feel you want to improve on the code, or would like to create a say... some React or JS library specifically for "searching" that does more than the 1-thing we learnt here. Feel free to [Clone OR Contribute](https://github.com/Emmaccen/search-algorithm) to the GitHub repo. 

If you do make a library that gives people that super-power, I'd like to feature you on this blog.

Cheers 🥂 and thanks for reading 😎.