Deep Copy

MediumPayPalTikTokByteDanceAmazon

Prompt

Write a function named deepCopy in JavaScript that creates a deep copy of an object. The function should handle nested objects and arrays within the object.

Example

const original = {
name: 'John',
age: 30,
hobbies: ['reading', 'gaming'],
address: {
city: 'New York',
zip: 10001,
},
};

const copy = deepCopy(original);
copy.hobbies.push('cooking');
copy.address.city = 'Los Angeles';

console.log(original.hobbies); // ['reading', 'gaming']
console.log(original.address.city); // 'New York'

Playground

Hint 1

Think about how to handle different types of values within the object. You'll need to handle arrays and nested objects separately.

Solution

Explanation

The solution works in three simple steps:

First, we check if the input is not an object (like a number, string, or null). If it's not an object, we return it directly:

if (typeof value !== 'object' || value === null) {
return value;
}

If the input is an array, we create a new array and copy each element:

if (Array.isArray(value)) {
return value.map((item) => deepCopy(item));
}

If the input is an object, we create a new object and copy all its properties:

return Object.fromEntries(
Object.entries(value).map(([key, value]) => [
key,
deepCopy(value),
])
);

The function calls itself (recursion) for each nested object or array it finds. This way, it creates a complete copy of the entire object structure, no matter how deep it goes.

In JavaScript, arrays are actually objects under the hood. That's why typeof [] returns 'object'. This is why we need to use Array.isArray() to specifically check if something is an array.

When working with objects in JavaScript, you have a few options for traversing them:

  1. Using for...in loop - This will go through all enumerable properties, including those inherited from the prototype chain.
  2. Using Object.keys() - This gives you an array of the object's own enumerable properties.
  3. Using Object.entries() - This gives you an array of key-value pairs for the object's own enumerable properties.

In our solution, we chose Object.entries() because:

  • It only looks at the object's own properties (not inherited ones)
  • It gives us both keys and values in a convenient format
  • It's perfect for creating a new object with Object.fromEntries()

Deep Copy Explained

Let's talk about deep copying in JavaScript! Ever notice how sometimes when you copy an object, changes to the copy affect the original? That's because JavaScript gives you a "shallow copy" by default.

Here's what I mean:

const original = {
name: 'Taylor',
hobbies: ['coding', 'baking'],
};
const shallowCopy = { ...original };

shallowCopy.hobbies.push('running');
console.log(original.hobbies); // ['coding', 'baking', 'running'] - Oops!

See that? We changed the copy, but it affected the original! That's because objects and arrays are stored by reference in JavaScript. When you do a shallow copy, you're just copying those references, not creating new objects.

That's where a deep copy comes in handy. It creates completely new copies of everything, all the way down!

How Our Solution Works

Our deepCopy function handles three scenarios:

  1. For primitives (strings, numbers, etc.):

    if (typeof value !== 'object' || value === null) {
    return value;
    }

    If it's not an object (or it's null), just return it as is. Primitives are always copied by value, so we're good!

  2. For arrays:

    if (Array.isArray(value)) {
    return value.map((item) => deepCopy(item));
    }

    We create a brand new array and fill it with deep copies of each item. This way each element gets properly copied too!

  3. For objects:

    return Object.fromEntries(
    Object.entries(value).map(([key, value]) => [
    key,
    deepCopy(value),
    ])
    );

    We create a completely new object and copy each property, making sure we deep copy each value too.

The magic here is recursion - the function calls itself for each nested object or array. No matter how deeply nested your data is, everything gets a proper copy!

When To Use Deep Copy

Deep copying is super useful when you need to:

  • Duplicate complex data without any connections to the original
  • Safely modify a copy without worrying about side effects
  • Create snapshots of data (like for undo/redo features)

But be careful! Deep copying large, complex objects can be expensive. If your object contains methods or circular references, you might need a more sophisticated approach like using the structured clone algorithm or a library like immer.

// Quick tip: For simple cases, you can also use:
const quickDeepCopy = JSON.parse(JSON.stringify(original));
// But this won't work for functions, dates, maps, sets, etc.

Now you've got the power to create truly independent copies of your data structures!

00:00