Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Support for _.cloneDeep using recursiveClone #306

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

zargold
Copy link

@zargold zargold commented Mar 15, 2021

Supports _.cloneDeep as discussed in Issue: #121.

  • Tests written
  • Documentation updated
  • added support per Object.fromEntries on MDN.

Copy link

@VitorLuizC VitorLuizC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would break if we pass null.

README.md Outdated Show resolved Hide resolved
tests/unit/all.js Outdated Show resolved Hide resolved
Raz Goldin and others added 2 commits March 15, 2021 16:51
Co-authored-by: Vitor Luiz Cavalcanti <vitorluizc@outlook.com>
Co-authored-by: Vitor Luiz Cavalcanti <vitorluizc@outlook.com>
@iandrewt
Copy link
Contributor

This breaks on an object with circular references

const object = {
  'bar': {},
  'foo': { 'b': { 'c': { 'd': {} } } },
};

object.foo.b.c.d = object;
object.bar.b = object.foo.b;

cloneDeep(object);
Uncaught RangeError: Maximum call stack size exceeded

@iandrewt
Copy link
Contributor

iandrewt commented Jul 7, 2021

Here's the cloneDeep function I'm using, courtesy of StackOverflow

function cloneDeep(obj, hash = new WeakMap()) {
  if (Object(obj) !== obj || obj instanceof Function) {
    return obj;
  }

  if (hash.has(obj)) {
    return hash.get(obj);
  }

  let result: any;

  try { // Try to run constructor without arguments
    result = new obj.constructor();
  } catch (e) {
    result = Object.create(Object.getPrototypeOf(obj));
  }

  if (obj instanceof Map) {
    Array.from(obj, ([key, val]) => result.set(cloneDeep(key, hash), cloneDeep(val, hash)));
  } else if (obj instanceof Set) {
    Array.from(obj, (key) => result.add(cloneDeep(key, hash)));
  }

  hash.set(obj, result);

  return Object.assign(result, ...Object.keys(obj).map(key => ({ [key]: cloneDeep(obj[key], hash) })));
};

@zargold
Copy link
Author

zargold commented Jul 7, 2021

Here's the cloneDeep function I'm using, courtesy of StackOverflow

function cloneDeep(obj, hash = new WeakMap()) {
  if (Object(obj) !== obj || obj instanceof Function) {
    return obj;
  }

  if (hash.has(obj)) {
    return hash.get(obj);
  }

  let result: any;

  try { // Try to run constructor without arguments
    result = new obj.constructor();
  } catch (e) {
    result = Object.create(Object.getPrototypeOf(obj));
  }

  if (obj instanceof Map) {
    Array.from(obj, ([key, val]) => result.set(cloneDeep(key, hash), cloneDeep(val, hash)));
  } else if (obj instanceof Set) {
    Array.from(obj, (key) => result.add(cloneDeep(key, hash)));
  }

  hash.set(obj, result);

  return Object.assign(result, ...Object.keys(obj).map(key => ({ [key]: cloneDeep(obj[key], hash) })));
};

Yes, if you care about this very unusual use case (that I've never used in 7 years of development). Very rarely do even libraries use this pattern of circular references then you could use a modified version as you quoted, but I doubt more than 1% of people will need that.

Just in case we want to support that: recursiveCloneWithCircularReference it would be easy:

const recursiveClone = (src, hash = new WeakMap()) => {

      if (src === null || typeof src !== 'object') { // for primitives / functions / non-references/pointers
        return src
      }
     if (hash.has(obj)) {
        return hash.get(obj);
      }
     let result;
      if (Array.isArray(src)) { // for arrays
        result = src.map(element => recursiveClone(element, hash))
      }
      result = Object.fromEntries(
        Object.entries(src).map(
          ([key, val]) => ([key, recursiveClone(val, hash)])
        )
      )
      hash.set(src, result);
      return result;
    }

In case we also want to support things like Maps & Sets we could too.

@iandrewt
Copy link
Contributor

iandrewt commented Jul 7, 2021

Unfortunately the project I was working on did need all the edge cases, but I'm glad there's a good simplified version

@stevemao stevemao requested a review from a team January 13, 2022 04:22
@kingyue737
Copy link
Contributor

Is structuredClone equal to deepClone?
https://developer.mozilla.org/en-US/docs/Web/API/structuredClone

@zargold
Copy link
Author

zargold commented Apr 8, 2022

Is structuredClone equal to deepClone?
https://developer.mozilla.org/en-US/docs/Web/API/structuredClone

Awesome find. I think people may complain that it lacks support for functions, but I don't often see the need for that piece of functionality.

@cht8687 cht8687 force-pushed the master branch 2 times, most recently from 5559a1c to bd9b25e Compare September 2, 2023 04:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants