1. Deep Object Comparison
Implementing a recursive function to compare primitives, arrays, and nested objects in JavaScript.
Watch section from 0:17 →Engineering video
17:48 · 19 June 2024
Implementing a recursive function to compare primitives, arrays, and nested objects in JavaScript.
Watch section from 0:17 →Checking types and comparing non-object primitive values using standard equality.
Watch section from 1:30 →Handling NaN values explicitly since JavaScript considers NaN never equal to itself.
Watch section from 2:50 →Validating array types and lengths before iterating through elements with recursive deep equal calls.
Watch section from 3:54 →Explaining that objects in JavaScript are equal by reference unless pointing to the same memory location.
Watch section from 6:00 →Extracting object keys, verifying key lengths, and recursively comparing properties.
Watch section from 8:00 →Using instanceof checks and string conversion to correctly compare date objects that have hidden keys.
Watch section from 10:24 →Handling null values separately since typeof null evaluates to object in JavaScript.
Watch section from 12:28 →Discussing how arrays are objects under the hood in JavaScript and the performance implications.
Watch section from 13:12 →Approaching interviews with a structured mindset, starting with simple base cases, and writing pseudo code.
Watch section from 14:47 →Today Bogdan is going to solve a very common JavaScript question elements get in live coding interviews. To keep it real, chat GPT and co-pilot are not allowed. Bogdan, I provided you with the starting boilerplate code and automated test, can you share your screen and let's get going. So what you see there, it's a function called deep equal. And that's what I want you to implement, I want you to implement a deep object function comparison in JavaScript.
So this function takes two arguments, those can be primitives, objects or arrays in JavaScript. And returns true or false if they are equal or not. Uh we are also going to review a few core fundamentals in JavaScript such as prototype inheritance and data data types in JavaScript, so be ready for that. Okay, so you mentioned we are comparing primitive values, arrays, and objects. And I would assume these objects can be nested? Yes.
They can be nested objects. Okay, so in that case, for objects, we will probably have to somehow go and run this function on the pretty much all the properties of that objects. It sounds like we will have to do some recursiveness. But for now, I won't worry about that. Let me run the test to have them running. Mhm. Okay. That looks very much together right now. Yeah. Okay, so you see you're comparing numbers, strings, booleans, um, okay.
So, I'm going to start just with the base case, which means we won't do any um, this function won't call itself for now. Um, probably I will layout a bit the different edge cases. Probably the easiest way to find out if they are not equal is to compare the type of the variable. Basically, we look at the type of object A. um primitives. So basically if it's a number string or boolean, and basically that would mean that it's not an object.
So none of them it's an object and none of them, yeah, none of them it's an object. That should do it. So I'll go ahead and say if the type of object A is not an object and the type of type B is not an object, then we can on our primitive comparison. So we would we'd assume here that they are primitives. And I will just return. So basically what we want to return to the not object so I could just string in and return object A equal equal to object B.
That would be our normal JavaScript compactor. Okay, so that was our primitives. Let me check the test really quick. Okay, so a lot of them pass now. We still have race and objects. I'm not going to function it. Okay, looks like we have test for dates and numbers with NaN. So let me solve this edge case with the NaN. So the issue is I as far as I recall a NaN it's never equal to a NaN in JS. That means even if we have two numbers that are both not a number, it will always return true.
Uh it will always return false. So I will have to write some custom logic for this edge case where we say if the object A uh actually let me check if this is a not number. So I'm going to check it's object A not a number. Um and so we check if object A it's not a number and we check if the if object B is not a number and we return through because they are both non then so let me return through. And ideally we make that test case pass.
Perfect. Okay, so we got primitives out of the way. The next one would be objects and arrays. But I see here also date objects. That looks like a bit of a edge case. Okay, so I'm going to go on with with arrays right now, okay? Just to follow our tests. ray, just to keep everything clean. Okay, so first of all, we want to check if both of them are rays. And after that, I would check they have the same length, because if they don't, then it's clear they're not equal, so we can just stop there. And then we just want to check, um, basically, we we make a one-to-one comparison of each element, so probably use a four loop to compare each item in the array. I would assuming the array we might have items that are objects or even other arrays. So for the comparison, I will call deep equal again. Okay, I'm going to go in and implement this and we see how that works out. So basically, we have array.t's array, which is allows us to actually check that something is an array. Because arrays are objects in JavaScript, so I'm going to check if object A it's an array, and if object B it's an array, perfect. And then we said, okay, let's check if they have the same length. If object A. length is not equal equal to object B. length, then return false, okay? So that's number one and now it looks like false for different arrays and false for different nested arrays. Okay, now this is when we actually do the for loop. So I'm going to write a for loop, we have our iterator, start at zero because the arrays, go as far as the length of the array, increment by one. And here, uh we want to invalidate, so if it's still we don't really care, but if deep equal of object A and object B at that position, if they're not equal, so if this is not true, then we stop the holding. So we found an element as not equal. Let me see if that works out. Okay. Yeah, it looks like it worked.
call itself, right? Which already has the capability to test primitives, right? Yes, or if we find a race inside a race, at still wrong. Okay, so yeah, it's recursion. Yes. I do believe the test might be incomplete here, because if we have objects, so inside imagine we have an array and then I have objects inside, it kind of looks like the test didn't cover that. I would expect that to be a false because we still have no object comparison and all the test on arrays return true. So I think there's no test for arrays inside uh objects inside an array. Uh maybe we can add that to the end. Okay, so let's move on with objects. This is where things are a bit more complicated because objects are equal by reference. Unless we're talking about the same object, it will it will always return false and that's why we let's do a key property uh check. So we basically check each property of the array if they're equal. And I think we can use dep equal on each property just like we did with arrays, but we iterate over properties, not values. Can you expand a bit expand a bit on this equal by reference for objects in JavaScript? What does that mean? Um it basically means that two to object are only equal to each other if they are pointing to the same objects in memory. So basically, unless they point to the same variable, they are two different objects. Even if they have the same properties or they look exactly the same, JavaScript says they're different if they don't refer to the same variable. So for example, constant A would not be constant equal to constant B right now in JavaScript, if we write it like this, it will only be equal if we do this. Gotcha. Nice. Cool. So if it refers back to a dev you question for the people watching us, uh guys, take note of this one. Perfect. So let's move on to objects. What do I want to do with objects? Well, we want to check if they are objects.
not arrays, and then we want to iterate over each property and run dep equal. Mhm. On it. Okay, so let's start with this one. Basically the type of object A to equal to let's say equal equal to object and so we just want to check that they are both objects, okay? So they're both objects. They are not so I'm going to copy this. So none of them is integrate, they are both objects. So now we go into the iteration. I'm going to extract the keys of both of them just to necessarily it's a bit easier I think if we have them here. So keys of A, just it makes statements a bit shorter down the road. This would be object A and the same with constant keys of B, not very original naming, but I guess we can go with it. And we get the keys. have these things in programming naming. Perfect. And now we can even check if the length of those is the same and return false if it's not because if they don't have the same length in their keys array. So I could do something like keys a. length is not equal equal to keys b. length, then just return false because that's a that's an obvious. So you always try to find the easiest way to invalidate the hypothesis that they are true. Okay. So now we want to iterate over I will use I will still use there's might as well it's to do this, but I'm just going to use um the traditional for loop. So while it's less than keys a. length and that would be a while plus plus. Perfect. And what do we want to do here? We want to if deep equal of Okay, what do we pass to deep equal? We want to pass property of object A. So object A of keys of A at I and pretty much we want the same property of B. So object B
should have to be the same property that we found in A and if they're not equal, we return false. Okay, so let's see. Okay, that went through. So let me just give you my code really quick. Not their objects, not arrays, and if they don't have the same length in the keys array then false, and then go over each key and compare the property at that key and return false if you find it. Okay, looks like we won we still have one S case which is comparing date objects.
This would be a tricky one. I'm probably going to want to want to do that before objects. What's the difference with date objects and normal objects in JavaScript? The problem is, I think data objects hide um a lot of the implementation. So there's a lot of hidden keys um that we cannot find when we do object. keys and that's where the difference of the date is. So I think if if we look at this test case, I would assume, let me find a date.
I would assume the one that fails is yeah, there's different dates, but our function says they're the same. Mhm Because the um the key or the property that contains this date information, it's hidden in the data object. When we do object. keys, we can really access it. Mhm So we need a different way to check for that. Uh, I think one of the easiest way is to convert the date to strings. First, we need to check check if they are both date objects.
Uh, we cannot use type of. I think we can use something like instance off, so we could do something like object A, it's an instance of date, which basically checks for the prototype. So it looks at the prototype of object A and figures out it's date at any point in the prototype chain of object A. And object B, pretty much the same. So we check that object B, it's also an instance of date. Perfect. And if they are both instance of date, then we want to check that they are not equal dates.
think we could do going to use the suggestion stance here, maybe get time or to string, for example, should work, or to UTC date. to it should work. So we we we just going to compare them as strings. Object A, today's string should not be equal to object B to object B. to date string, and then we return false. This will parater. Okay, nice. Returns two for new values. We still have a missing test.
So, okay, we're not handling null. Uh okay, I'm going to handle null here. Why? Because null is actually of type object. But if we if they're both null, then then we just return immediately. So it's a bit of an edge case. So I'll say if object A, it's equal equal to null, and object is null, then just return to. I think that might be the last edge case. Let's see. Wow. And voila, okay. Voila, nice.
Well done, Bogdan. Looks pretty amazing. Cool, what's the what's the one thing that you would tell developers that face these kind of problems in live coding interviews? You know, what would be the most important pitfalls you would actually keep in mind as you as you solve this thing. Cool, so you've got it now, is there a way we can actually optimize this solution? I think arrays are actually objects under the hood in JavaScript.
Which means we don't really need this extra logic. I think I can even take this out. And we apply, I'm going to comment this line out. So, this code runs, also if they are rays. And there you go. It's still passed. So, can you expand a bit on this idea that arrays are actually objects in JavaScript? Um, yeah, sure. So, the type the type of an array in JavaScript, it's object and like the underlying data structure.
It's an object. And if we wanted to have real arrays that then have all these push methods and pop methods, we probably will have to use like more for native data structure, like a buffer array. We'll probably work in OJS. any uh performance implications of this. Um, yeah, so of course having all these rappers around the race means you'll use a lot of performance and also, in many cases, you get the performance of a hash map.
Uh, of course they are very optimized, but you definitely lose performance, if you had a very um, to much for memory and performance, I would probably go for something like a buffer array. So if you have a huge array, you want to store that in memory and you want to store it efficiently, you're better off using something like a native array structure. What would be the pitfalls for a, you know, mid-level, even senior dev, junior dev, going into this kind of live coding JavaScript interviews?
What would you tell them, hey, you know, look out for this and this thing for you to actually be be successful and, you know, get the interview to an end? Because a lot of people actually struggle with just going directly blank, not having a mental structure to to get the problem to an end. Um, sure, so number one, as you've seen, I did even if I've seen this problem before, I always write pseudo code.
The other thing, if you are some people are mid-level, almost senior, and the biggest mistake that it's they'll jump right in and say, oh, I know the solution, and even if I would know the solution, I would still go step by step. And very important, start with simple first. So as you see here, I always start with a simplest base case and kind of solve those ones because first of all, that builds confidence when you're doing it, because you're like, you see the test passing.
If there's no test, um, I would advise you to write some yourself just uh at the beginning of the interview. But ideally they give you some test. The moment you make some of them pass, you already feel really good and that confidence makes you focus more on the problem and not on, oh my god, what am I doing here? So start with a simple stuff things like this that are edge cases. If you don't know it, I kind of I know JavaScript very well and I know those things ahead or this detail with a N, you can just ask.
But before you ask, if you don't want to look like a junior, just write down your intention like want to check if if the numbers are N. And then then the interviewer can help you out, but it still looks like you know where you're going. So, as you see, I always try to write as much as I can, the reason is they'll hang up the interview and the only thing they have from your performance, it's the impression of you, which will fade away very fast, but also the code you'll have behind. So the more you write and the more you think in writing, the better.
You want to talk out loud, but you also want to write in the same time you are talking out loud. If you're not touch typing already, make sure you also learn touch typing for the interviews. It's a crucial skill. Okay, folks, so that's a lot of things I would add. Make sure you are writing things down like Bogdan mentioned, and number two, make sure you also verbalize that because you are not alone.
Right, and maybe um you know, maybe there's there are a lot of things going into your head, unless you verbalize them, unless you tell them to the interviewer, uh they won't know about it. So it will seem like, you know, you're thinking a lot, but you cannot get a lot done, which would be a pity because maybe you have those things in your uh in your mind. And this is something that we work a lot with the developers we coach and we help them actually verbalize these things and and put in place this structured way when approaching a live coding interview.
And and if you get something out of this video, it's that beyond the implementation of the deep equal, the comparison and the details about the JavaScript language, it's how Bogdan actually approached the interview. We will see you folks in the next one and make sure you subscribe to the senior dev channel and that gives us the strength energy to make more of these videos and let them come your way.
Best of luck if you're interviewing right now and we will see you in the next one.