feat(utils): cloneCyclcicObject new util

This commit is contained in:
Carlos Garcia Ortiz karliatto
2024-11-25 17:18:04 +01:00
committed by martin
parent 5253a9daec
commit a65cc86086
3 changed files with 109 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
// Makes a deep copy of an object, handling cyclical references.
export const cloneObjectCyclic = <T>(obj: T, seen = new WeakMap<object, any>()): T => {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (seen.has(obj)) {
return seen.get(obj);
}
if (obj instanceof ArrayBuffer) {
return obj.slice(0) as any;
}
if (ArrayBuffer.isView(obj)) {
const TypedArrayConstructor = obj.constructor as new (...args: any[]) => typeof obj;
return new TypedArrayConstructor(obj) as any;
}
const clone: any = Array.isArray(obj) ? [] : {};
seen.set(obj, clone);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
const value = (obj as any)[key];
if (typeof value === 'function' || typeof value === 'symbol') {
continue;
}
(clone as any)[key] = cloneObjectCyclic(value, seen);
}
}
return clone;
};

View File

@@ -49,3 +49,4 @@ export * from './throttler';
export * from './extractUrlsFromText';
export * from './isFullPath';
export * from './asciiUtils';
export * from './cloneObjectCyclic';

View File

@@ -0,0 +1,71 @@
import { cloneObjectCyclic } from '../src/cloneObjectCyclic';
describe('cloneObjectCyclic', () => {
describe('deep cloning of objects', () => {
it('should clone a simple object', () => {
const original = { a: 1, b: 2 };
const cloned = cloneObjectCyclic(original);
expect(cloned).toEqual(original);
expect(cloned).not.toBe(original);
});
it('should clone an object with nested properties', () => {
const original = { a: { b: { c: 3 } } };
const cloned = cloneObjectCyclic(original);
expect(cloned).toEqual(original);
expect(cloned.a).not.toBe(original.a);
});
it('should handle cyclical references', () => {
const original = { a: 1 };
// @ts-expect-error"
original['cyclical'] = original;
const cloned = cloneObjectCyclic(original);
expect(cloned).toEqual(original);
// @ts-expect-error"
expect(cloned['cyclical']).toBe(cloned);
});
it('should clone arrays', () => {
const original = [1, 2, 3, { a: 4 }];
const cloned = cloneObjectCyclic(original);
expect(cloned).toEqual(original);
expect(cloned).not.toBe(original);
expect(cloned[3]).not.toBe(original[3]);
});
it('should handle arrays with cyclical references', () => {
const original = [1, 2, 3];
// @ts-expect-error"
original.push(original);
const cloned = cloneObjectCyclic(original);
expect(cloned).toEqual(original);
expect(cloned[3]).toBe(cloned);
});
it('should clone an object with functions and symbols', () => {
const original = {
a: 1,
b: () => {},
c: Symbol('symbol'),
};
const cloned = cloneObjectCyclic(original);
expect(cloned).toEqual({ a: 1 });
expect(cloned).not.toBe(original);
});
it('should clone an ArrayBuffer', () => {
const original = new ArrayBuffer(8);
const cloned = cloneObjectCyclic(original);
expect(cloned).not.toBe(original);
expect(cloned.byteLength).toBe(original.byteLength);
});
it('should clone typed arrays', () => {
const original = new Uint8Array([1, 2, 3]);
const cloned = cloneObjectCyclic(original);
expect(cloned).not.toBe(original);
expect(cloned).toEqual(original);
});
});
});