Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 44 additions & 12 deletions 01-deep-clone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,65 @@
* @returns {*} A deep clone of the input value
*/
function deepClone(value, visited = new WeakMap()) {
// TODO: Implement deep cloning

// Step 1: Handle primitives (return as-is)
// Primitives: null, undefined, number, string, boolean, symbol, bigint
if (value === null || typeof value !== 'object') {
return value;
}

// Step 2: Check for circular references using the visited WeakMap
// If we've seen this object before, return the cached clone
if (visited.has(value)) {
return visited.get(value);
}

// Step 3: Handle Date objects
// Create a new Date with the same time value
if (value instanceof Date) {
return new Date(value.getTime());
}

// Step 4: Handle RegExp objects
// Create a new RegExp with the same source and flags
if (value instanceof RegExp) {
return new RegExp(value.source, value.flags);
}

// Step 5: Handle Map objects
// Create a new Map and deep clone each key-value pair
if (value instanceof Map) {
const clonedMap = new Map();
visited.set(value, clonedMap);
for (const [key, val] of value) {
clonedMap.set(deepClone(key, visited), deepClone(val, visited));
}
return clonedMap;
}

// Step 6: Handle Set objects
// Create a new Set and deep clone each value
if (value instanceof Set) {
const clonedSet = new Set();
visited.set(value, clonedSet);
for (const val of value) {
clonedSet.add(deepClone(val, visited));
}
return clonedSet;
}

// Step 7: Handle Arrays
// Create a new array and deep clone each element
if (Array.isArray(value)) {
const clonedArray = [];
visited.set(value, clonedArray);
for (let i = 0; i < value.length; i++) {
clonedArray[i] = deepClone(value[i], visited);
}
return clonedArray;
}

// Step 8: Handle plain Objects
// Create a new object and deep clone each property

return undefined; // Broken: Replace with your implementation
const clonedObj = {};
visited.set(value, clonedObj);
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
clonedObj[key] = deepClone(value[key], visited);
}
}
return clonedObj;
}

module.exports = { deepClone };
61 changes: 32 additions & 29 deletions 02-debounce-throttle/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@
* @returns {Function} The debounced function with a cancel() method
*/
function debounce(fn, delay) {
// TODO: Implement debounce
let timeoutId = null;

// Step 1: Create a variable to store the timeout ID
function debounced(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn.apply(this, args);
}, delay);
}

// Step 2: Create the debounced function that:
// - Clears any existing timeout
// - Sets a new timeout to call fn after delay
// - Preserves `this` context and arguments
debounced.cancel = function () {
clearTimeout(timeoutId);
timeoutId = null;
};

// Step 3: Add a cancel() method to clear pending timeout

// Step 4: Return the debounced function

// Return a placeholder that doesn't work
throw new Error("Not implemented");
return debounced;
}

/**
Expand All @@ -37,23 +37,26 @@ function debounce(fn, delay) {
* @returns {Function} The throttled function with a cancel() method
*/
function throttle(fn, limit) {
// TODO: Implement throttle

// Step 1: Create variables to track:
// - Whether we're currently in a throttle period
// - The timeout ID for cleanup

// Step 2: Create the throttled function that:
// - If not throttling, execute fn immediately and start throttle period
// - If throttling, ignore the call
// - Preserves `this` context and arguments

// Step 3: Add a cancel() method to reset throttle state

// Step 4: Return the throttled function

// Return a placeholder that doesn't work
throw new Error("Not implemented");
let isThrottling = false;
let timeoutId = null;

function throttled(...args) {
if (!isThrottling) {
fn.apply(this, args);
isThrottling = true;
timeoutId = setTimeout(() => {
isThrottling = false;
}, limit);
}
}

throttled.cancel = function () {
clearTimeout(timeoutId);
isThrottling = false;
timeoutId = null;
};

return throttled;
}

module.exports = { debounce, throttle };
34 changes: 16 additions & 18 deletions 03-custom-bind/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,27 @@
* @returns {Function} A new bound function
*/
function customBind(fn, context, ...boundArgs) {
// TODO: Implement custom bind
if (typeof fn !== "function") {
throw new TypeError("customBind requires a function");
}

// Step 1: Validate that fn is a function
// Throw TypeError if not
function boundFunction(...args) {
const allArgs = [...boundArgs, ...args];

// Step 2: Create the bound function
// It should:
// - Combine boundArgs with any new arguments
// - Call the original function with the combined arguments
// - Use the correct `this` context
// Check if called as constructor (with new)
if (new.target) {
return fn.apply(this, allArgs);
}

// Step 3: Handle constructor calls (when used with `new`)
// When called as a constructor:
// - `this` should be a new instance, not the bound context
// - The prototype chain should be preserved
return fn.apply(context, allArgs);
}

// Step 4: Preserve the prototype for constructor usage
// boundFunction.prototype = Object.create(fn.prototype)
// Preserve prototype for constructor usage
if (fn.prototype) {
boundFunction.prototype = Object.create(fn.prototype);
}

// Step 5: Return the bound function

// Return placeholder that doesn't work
throw new Error("Not implemented");
return boundFunction;
}

/**
Expand Down
62 changes: 33 additions & 29 deletions 04-memoization/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,50 @@
* @returns {Function} Memoized function with cache control methods
*/
function memoize(fn, options = {}) {
// TODO: Implement memoization
const { maxSize, ttl, keyGenerator } = options;
const cache = new Map();

// Step 1: Extract options with defaults
// const { maxSize, ttl, keyGenerator } = options;
const defaultKeyGenerator = (args) => JSON.stringify(args);
const getKey = keyGenerator || defaultKeyGenerator;

// Step 2: Create the cache (use Map for ordered keys)
// const cache = new Map();
function memoized(...args) {
const key = getKey(args);

// Step 3: Create default key generator
// Default: JSON.stringify(args) or args.join(',')
if (cache.has(key)) {
const entry = cache.get(key);

// Step 4: Create the memoized function
// - Generate cache key from arguments
// - Check if key exists and is not expired (TTL)
// - If cached, return cached value
// - If not cached, call fn and store result
// - Handle maxSize eviction (remove oldest)
// Check TTL expiration
if (ttl && Date.now() - entry.timestamp > ttl) {
cache.delete(key);
} else {
return entry.value;
}
}

// Step 5: Add cache control methods
// memoized.cache = {
// clear: () => cache.clear(),
// delete: (key) => cache.delete(key),
// has: (key) => cache.has(key),
// get size() { return cache.size; }
// };
// Call original function
const result = fn.apply(this, args);

// Step 6: Return memoized function
// Evict oldest if maxSize reached
if (maxSize && cache.size >= maxSize) {
const oldestKey = cache.keys().next().value;
cache.delete(oldestKey);
}

// Store result with timestamp
cache.set(key, { value: result, timestamp: Date.now() });

return result;
}

// Return placeholder that doesn't work
const memoized = function () {
return undefined;
};
memoized.cache = {
clear: () => {},
delete: () => false,
has: () => false,
clear: () => cache.clear(),
delete: (key) => cache.delete(key),
has: (key) => cache.has(key),
get size() {
return -1;
return cache.size;
},
};

return memoized;
}

Expand Down
Loading