diff --git a/src/03-array/11-array-chunking.js b/src/03-array/11-array-chunking.js new file mode 100644 index 00000000..d695defa --- /dev/null +++ b/src/03-array/11-array-chunking.js @@ -0,0 +1,93 @@ +// src/03-array/11-array-chunking.js + +/** + * Array Chunking - Split an array into chunks of a specified size + * Common use case: Pagination, batch processing, data visualization + */ + +// Approach 1: Using slice method +function chunkArray(array, chunkSize) { + if (chunkSize <= 0) { + throw new Error('Chunk size must be greater than 0'); + } + + const result = []; + for (let i = 0; i < array.length; i += chunkSize) { + result.push(array.slice(i, i + chunkSize)); + } + return result; +} + +// Approach 2: Using splice method (modifies original array) +function chunkArraySplice(array, chunkSize) { + if (chunkSize <= 0) { + throw new Error('Chunk size must be greater than 0'); + } + + const result = []; + const arrayCopy = [...array]; // Create a copy to avoid modifying original + + while (arrayCopy.length > 0) { + result.push(arrayCopy.splice(0, chunkSize)); + } + return result; +} + +// Approach 3: Using reduce method (functional approach) +function chunkArrayReduce(array, chunkSize) { + if (chunkSize <= 0) { + throw new Error('Chunk size must be greater than 0'); + } + + return array.reduce((chunks, item, index) => { + const chunkIndex = Math.floor(index / chunkSize); + + if (!chunks[chunkIndex]) { + chunks[chunkIndex] = []; // Start a new chunk + } + + chunks[chunkIndex].push(item); + return chunks; + }, []); +} + +// Examples +console.log('=== Array Chunking Examples ===\n'); + +const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +console.log('Original array:', numbers); + +console.log('\nChunk size 3 (slice method):', chunkArray(numbers, 3)); +// Output: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]] + +console.log('Chunk size 4 (splice method):', chunkArraySplice(numbers, 4)); +// Output: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]] + +console.log('Chunk size 2 (reduce method):', chunkArrayReduce(numbers, 2)); +// Output: [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] + +// Real-world example: Paginating data +const users = [ + 'Alice', 'Bob', 'Charlie', 'David', 'Eve', + 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack' +]; + +const itemsPerPage = 3; +const pages = chunkArray(users, itemsPerPage); + +console.log('\n=== Pagination Example ==='); +console.log(`Total users: ${users.length}`); +console.log(`Items per page: ${itemsPerPage}`); +console.log(`Total pages: ${pages.length}\n`); + +pages.forEach((page, index) => { + console.log(`Page ${index + 1}:`, page); +}); + +// Edge cases +console.log('\n=== Edge Cases ==='); +console.log('Empty array:', chunkArray([], 3)); +console.log('Chunk size larger than array:', chunkArray([1, 2], 5)); +console.log('Chunk size of 1:', chunkArray([1, 2, 3], 1)); + +// to see the output of this file use the command: node src/03-array/11-array-chunking.js diff --git a/src/03-array/11-array-chunking.ts b/src/03-array/11-array-chunking.ts new file mode 100644 index 00000000..eafb10d5 --- /dev/null +++ b/src/03-array/11-array-chunking.ts @@ -0,0 +1,93 @@ +// src/03-array/11-array-chunking.ts + +/** + * Array Chunking - Split an array into chunks of a specified size + * Common use case: Pagination, batch processing, data visualization + */ + +// Approach 1: Using slice method +function chunkArray(array: T[], chunkSize: number): T[][] { + if (chunkSize <= 0) { + throw new Error('Chunk size must be greater than 0'); + } + + const result: T[][] = []; + for (let i = 0; i < array.length; i += chunkSize) { + result.push(array.slice(i, i + chunkSize)); + } + return result; +} + +// Approach 2: Using splice method (modifies original array) +function chunkArraySplice(array: T[], chunkSize: number): T[][] { + if (chunkSize <= 0) { + throw new Error('Chunk size must be greater than 0'); + } + + const result: T[][] = []; + const arrayCopy = [...array]; // Create a copy to avoid modifying original + + while (arrayCopy.length > 0) { + result.push(arrayCopy.splice(0, chunkSize)); + } + return result; +} + +// Approach 3: Using reduce method (functional approach) +function chunkArrayReduce(array: T[], chunkSize: number): T[][] { + if (chunkSize <= 0) { + throw new Error('Chunk size must be greater than 0'); + } + + return array.reduce((chunks: T[][], item: T, index: number) => { + const chunkIndex = Math.floor(index / chunkSize); + + if (!chunks[chunkIndex]) { + chunks[chunkIndex] = []; // Start a new chunk + } + + chunks[chunkIndex].push(item); + return chunks; + }, []); +} + +// Examples +console.log('=== Array Chunking Examples ===\n'); + +const numbers: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +console.log('Original array:', numbers); + +console.log('\nChunk size 3 (slice method):', chunkArray(numbers, 3)); +// Output: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]] + +console.log('Chunk size 4 (splice method):', chunkArraySplice(numbers, 4)); +// Output: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]] + +console.log('Chunk size 2 (reduce method):', chunkArrayReduce(numbers, 2)); +// Output: [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] + +// Real-world example: Paginating data +const users: string[] = [ + 'Alice', 'Bob', 'Charlie', 'David', 'Eve', + 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack' +]; + +const itemsPerPage = 3; +const pages = chunkArray(users, itemsPerPage); + +console.log('\n=== Pagination Example ==='); +console.log(`Total users: ${users.length}`); +console.log(`Items per page: ${itemsPerPage}`); +console.log(`Total pages: ${pages.length}\n`); + +pages.forEach((page, index) => { + console.log(`Page ${index + 1}:`, page); +}); + +// Edge cases +console.log('\n=== Edge Cases ==='); +console.log('Empty array:', chunkArray([], 3)); +console.log('Chunk size larger than array:', chunkArray([1, 2], 5)); +console.log('Chunk size of 1:', chunkArray([1, 2, 3], 1)); + +// to see the output of this file use the command: node src/03-array/11-array-chunking.ts diff --git a/src/03-array/12-flatten-arrays.js b/src/03-array/12-flatten-arrays.js new file mode 100644 index 00000000..5b13e423 --- /dev/null +++ b/src/03-array/12-flatten-arrays.js @@ -0,0 +1,129 @@ +// src/03-array/12-flatten-arrays.js + +/** + * Flatten Nested Arrays - Convert multi-dimensional arrays into a single-dimensional array + * Common use case: Data processing, tree traversal results, nested data structures + */ + +// Approach 1: Using flat() method (ES2019+) +function flattenSimple(array, depth = 1) { + return array.flat(depth); +} + +// Approach 2: Deep flatten using flat(Infinity) +function flattenDeep(array) { + return array.flat(Infinity); +} + +// Approach 3: Recursive approach (custom implementation) +function flattenRecursive(array) { + const result = []; + + for (let item of array) { + if (Array.isArray(item)) { + result.push(...flattenRecursive(item)); + } else { + result.push(item); + } + } + + return result; +} + +// Approach 4: Using reduce (functional approach) +function flattenReduce(array) { + return array.reduce((acc, item) => { + return acc.concat(Array.isArray(item) ? flattenReduce(item) : item); + }, []); +} + +// Approach 5: Iterative approach using stack +function flattenIterative(array) { + const stack = [...array]; + const result = []; + + while (stack.length) { + const item = stack.pop(); + + if (Array.isArray(item)) { + stack.push(...item); + } else { + result.unshift(item); // Add to beginning since we're popping from end + } + } + + return result; +} + +// Examples +console.log('=== Flatten Arrays Examples ===\n'); + +const nestedArray = [1, [2, 3], [4, [5, 6]], [[[7]]], 8]; +console.log('Original nested array:', JSON.stringify(nestedArray)); + +console.log('\nFlat with depth 1:', flattenSimple(nestedArray, 1)); +// Output: [1, 2, 3, 4, [5, 6], [[7]], 8] + +console.log('Flat with depth 2:', flattenSimple(nestedArray, 2)); +// Output: [1, 2, 3, 4, 5, 6, [7], 8] + +console.log('Deep flatten (Infinity):', flattenDeep(nestedArray)); +// Output: [1, 2, 3, 4, 5, 6, 7, 8] + +console.log('Recursive flatten:', flattenRecursive(nestedArray)); +// Output: [1, 2, 3, 4, 5, 6, 7, 8] + +console.log('Reduce flatten:', flattenReduce(nestedArray)); +// Output: [1, 2, 3, 4, 5, 6, 7, 8] + +console.log('Iterative flatten:', flattenIterative(nestedArray)); +// Output: [1, 2, 3, 4, 5, 6, 7, 8] + +// Real-world example: Flattening category hierarchies +console.log('\n=== Real-World Example: Category Hierarchy ==='); + +const categories = [ + 'Electronics', + ['Computers', ['Laptops', 'Desktops', ['Gaming PCs', 'Workstations']]], + ['Mobile', ['Smartphones', 'Tablets']], + 'Books', + ['Fiction', ['Sci-Fi', 'Fantasy']] +]; + +console.log('Original categories:', JSON.stringify(categories)); +console.log('Flattened categories:', flattenDeep(categories)); + +// Performance comparison +console.log('\n=== Performance Comparison ==='); + +const largeNestedArray = [ + [1, 2, [3, 4]], + [5, [6, [7, 8]]], + [[9, 10], 11], + [12, [13, [14, [15]]]] +]; + +console.time('flat(Infinity)'); +flattenDeep(largeNestedArray); +console.timeEnd('flat(Infinity)'); + +console.time('Recursive'); +flattenRecursive(largeNestedArray); +console.timeEnd('Recursive'); + +console.time('Reduce'); +flattenReduce(largeNestedArray); +console.timeEnd('Reduce'); + +console.time('Iterative'); +flattenIterative(largeNestedArray); +console.timeEnd('Iterative'); + +// Edge cases +console.log('\n=== Edge Cases ==='); +console.log('Empty array:', flattenDeep([])); +console.log('Already flat array:', flattenDeep([1, 2, 3, 4])); +console.log('Array with empty arrays:', flattenDeep([1, [], [2, []], 3])); +console.log('Mixed types:', flattenDeep([1, 'hello', [true, [null, undefined]], { key: 'value' }])); + +// to see the output of this file use the command: node src/03-array/12-flatten-arrays.js diff --git a/src/03-array/12-flatten-arrays.ts b/src/03-array/12-flatten-arrays.ts new file mode 100644 index 00000000..6162502e --- /dev/null +++ b/src/03-array/12-flatten-arrays.ts @@ -0,0 +1,129 @@ +// src/03-array/12-flatten-arrays.ts + +/** + * Flatten Nested Arrays - Convert multi-dimensional arrays into a single-dimensional array + * Common use case: Data processing, tree traversal results, nested data structures + */ + +// Approach 1: Using flat() method (ES2019+) +function flattenSimple(array: any[], depth: number = 1): T[] { + return array.flat(depth); +} + +// Approach 2: Deep flatten using flat(Infinity) +function flattenDeep(array: any[]): T[] { + return array.flat(Infinity); +} + +// Approach 3: Recursive approach (custom implementation) +function flattenRecursive(array: any[]): T[] { + const result: T[] = []; + + for (let item of array) { + if (Array.isArray(item)) { + result.push(...flattenRecursive(item)); + } else { + result.push(item); + } + } + + return result; +} + +// Approach 4: Using reduce (functional approach) +function flattenReduce(array: any[]): T[] { + return array.reduce((acc: T[], item: any) => { + return acc.concat(Array.isArray(item) ? flattenReduce(item) : item); + }, []); +} + +// Approach 5: Iterative approach using stack +function flattenIterative(array: any[]): T[] { + const stack = [...array]; + const result: T[] = []; + + while (stack.length) { + const item = stack.pop(); + + if (Array.isArray(item)) { + stack.push(...item); + } else { + result.unshift(item); // Add to beginning since we're popping from end + } + } + + return result; +} + +// Examples +console.log('=== Flatten Arrays Examples ===\n'); + +const nestedArray: any[] = [1, [2, 3], [4, [5, 6]], [[[7]]], 8]; +console.log('Original nested array:', JSON.stringify(nestedArray)); + +console.log('\nFlat with depth 1:', flattenSimple(nestedArray, 1)); +// Output: [1, 2, 3, 4, [5, 6], [[7]], 8] + +console.log('Flat with depth 2:', flattenSimple(nestedArray, 2)); +// Output: [1, 2, 3, 4, 5, 6, [7], 8] + +console.log('Deep flatten (Infinity):', flattenDeep(nestedArray)); +// Output: [1, 2, 3, 4, 5, 6, 7, 8] + +console.log('Recursive flatten:', flattenRecursive(nestedArray)); +// Output: [1, 2, 3, 4, 5, 6, 7, 8] + +console.log('Reduce flatten:', flattenReduce(nestedArray)); +// Output: [1, 2, 3, 4, 5, 6, 7, 8] + +console.log('Iterative flatten:', flattenIterative(nestedArray)); +// Output: [1, 2, 3, 4, 5, 6, 7, 8] + +// Real-world example: Flattening category hierarchies +console.log('\n=== Real-World Example: Category Hierarchy ==='); + +const categories: any[] = [ + 'Electronics', + ['Computers', ['Laptops', 'Desktops', ['Gaming PCs', 'Workstations']]], + ['Mobile', ['Smartphones', 'Tablets']], + 'Books', + ['Fiction', ['Sci-Fi', 'Fantasy']] +]; + +console.log('Original categories:', JSON.stringify(categories)); +console.log('Flattened categories:', flattenDeep(categories)); + +// Performance comparison +console.log('\n=== Performance Comparison ==='); + +const largeNestedArray: any[] = [ + [1, 2, [3, 4]], + [5, [6, [7, 8]]], + [[9, 10], 11], + [12, [13, [14, [15]]]] +]; + +console.time('flat(Infinity)'); +flattenDeep(largeNestedArray); +console.timeEnd('flat(Infinity)'); + +console.time('Recursive'); +flattenRecursive(largeNestedArray); +console.timeEnd('Recursive'); + +console.time('Reduce'); +flattenReduce(largeNestedArray); +console.timeEnd('Reduce'); + +console.time('Iterative'); +flattenIterative(largeNestedArray); +console.timeEnd('Iterative'); + +// Edge cases +console.log('\n=== Edge Cases ==='); +console.log('Empty array:', flattenDeep([])); +console.log('Already flat array:', flattenDeep([1, 2, 3, 4])); +console.log('Array with empty arrays:', flattenDeep([1, [], [2, []], 3])); +console.log('Mixed types:', flattenDeep([1, 'hello', [true, [null, undefined]], { key: 'value' }])); + +// to see the output of this file use the command: node src/03-array/12-flatten-arrays.ts diff --git a/src/03-array/13-remove-duplicates.js b/src/03-array/13-remove-duplicates.js new file mode 100644 index 00000000..2fdb72ed --- /dev/null +++ b/src/03-array/13-remove-duplicates.js @@ -0,0 +1,151 @@ +// src/03-array/13-remove-duplicates.js + +/** + * Remove Duplicates from Arrays - Multiple approaches to eliminate duplicate values + * Common use case: Data cleaning, unique value extraction, filtering + */ + +// Approach 1: Using Set (most efficient for primitives) +function removeDuplicatesSet(array) { + return [...new Set(array)]; +} + +// Approach 2: Using filter and indexOf +function removeDuplicatesFilter(array) { + return array.filter((item, index) => array.indexOf(item) === index); +} + +// Approach 3: Using reduce +function removeDuplicatesReduce(array) { + return array.reduce((unique, item) => { + return unique.includes(item) ? unique : [...unique, item]; + }, []); +} + +// Approach 4: Using a Map (preserves insertion order) +function removeDuplicatesMap(array) { + const map = new Map(); + array.forEach(item => map.set(item, true)); + return Array.from(map.keys()); +} + +// Approach 5: Traditional loop approach +function removeDuplicatesLoop(array) { + const unique = []; + for (let i = 0; i < array.length; i++) { + if (!unique.includes(array[i])) { + unique.push(array[i]); + } + } + return unique; +} + +// Approach 6: Remove duplicates from array of objects (by property) +function removeDuplicatesByProperty(array, property) { + const seen = new Set(); + return array.filter(item => { + const value = item[property]; + if (seen.has(value)) { + return false; + } + seen.add(value); + return true; + }); +} + +// Examples +console.log('=== Remove Duplicates Examples ===\n'); + +const numbers = [1, 2, 3, 2, 4, 3, 5, 1, 6, 4]; +console.log('Original array:', numbers); + +console.log('\nUsing Set:', removeDuplicatesSet(numbers)); +// Output: [1, 2, 3, 4, 5, 6] + +console.log('Using filter:', removeDuplicatesFilter(numbers)); +// Output: [1, 2, 3, 4, 5, 6] + +console.log('Using reduce:', removeDuplicatesReduce(numbers)); +// Output: [1, 2, 3, 4, 5, 6] + +console.log('Using Map:', removeDuplicatesMap(numbers)); +// Output: [1, 2, 3, 4, 5, 6] + +console.log('Using loop:', removeDuplicatesLoop(numbers)); +// Output: [1, 2, 3, 4, 5, 6] + +// String array example +console.log('\n=== String Array Example ==='); +const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'grape', 'apple']; +console.log('Original:', fruits); +console.log('Unique:', removeDuplicatesSet(fruits)); +// Output: ['apple', 'banana', 'orange', 'grape'] + +// Real-world example: Remove duplicate objects by property +console.log('\n=== Remove Duplicates from Objects ==='); + +const users = [ + { id: 1, name: 'Alice', email: 'alice@example.com' }, + { id: 2, name: 'Bob', email: 'bob@example.com' }, + { id: 1, name: 'Alice Smith', email: 'alice@example.com' }, // duplicate id + { id: 3, name: 'Charlie', email: 'charlie@example.com' }, + { id: 2, name: 'Bob Johnson', email: 'bob@example.com' } // duplicate id +]; + +console.log('Original users:', users.length); +const uniqueUsersById = removeDuplicatesByProperty(users, 'id'); +console.log('Unique users by id:', uniqueUsersById.length); +console.log(uniqueUsersById); + +const uniqueUsersByEmail = removeDuplicatesByProperty(users, 'email'); +console.log('\nUnique users by email:', uniqueUsersByEmail.length); +console.log(uniqueUsersByEmail); + +// Performance comparison +console.log('\n=== Performance Comparison ==='); + +const largeArray = Array.from({ length: 10000 }, () => Math.floor(Math.random() * 1000)); + +console.time('Set method'); +removeDuplicatesSet(largeArray); +console.timeEnd('Set method'); + +console.time('Filter method'); +removeDuplicatesFilter(largeArray); +console.timeEnd('Filter method'); + +console.time('Reduce method'); +removeDuplicatesReduce(largeArray); +console.timeEnd('Reduce method'); + +console.time('Map method'); +removeDuplicatesMap(largeArray); +console.timeEnd('Map method'); + +console.time('Loop method'); +removeDuplicatesLoop(largeArray); +console.timeEnd('Loop method'); + +// Edge cases +console.log('\n=== Edge Cases ==='); +console.log('Empty array:', removeDuplicatesSet([])); +console.log('No duplicates:', removeDuplicatesSet([1, 2, 3, 4])); +console.log('All duplicates:', removeDuplicatesSet([5, 5, 5, 5])); +console.log('Mixed types:', removeDuplicatesSet([1, '1', 2, '2', true, 'true'])); +console.log('With null/undefined:', removeDuplicatesSet([1, null, 2, undefined, null, 3, undefined])); + +// Count duplicates +console.log('\n=== Count Duplicates ==='); +function countDuplicates(array) { + const counts = {}; + array.forEach(item => { + counts[item] = (counts[item] || 0) + 1; + }); + return counts; +} + +const testArray = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]; +console.log('Array:', testArray); +console.log('Duplicate counts:', countDuplicates(testArray)); + +// to see the output of this file use the command: node src/03-array/13-remove-duplicates.js diff --git a/src/03-array/13-remove-duplicates.ts b/src/03-array/13-remove-duplicates.ts new file mode 100644 index 00000000..9cc92aca --- /dev/null +++ b/src/03-array/13-remove-duplicates.ts @@ -0,0 +1,160 @@ +// src/03-array/13-remove-duplicates.ts + +/** + * Remove Duplicates from Arrays - Multiple approaches to eliminate duplicate values + * Common use case: Data cleaning, unique value extraction, filtering + */ + +// Approach 1: Using Set (most efficient for primitives) +function removeDuplicatesSet(array: T[]): T[] { + return [...new Set(array)]; +} + +// Approach 2: Using filter and indexOf +function removeDuplicatesFilter(array: T[]): T[] { + return array.filter((item, index) => array.indexOf(item) === index); +} + +// Approach 3: Using reduce +function removeDuplicatesReduce(array: T[]): T[] { + return array.reduce((unique: T[], item: T) => { + return unique.includes(item) ? unique : [...unique, item]; + }, []); +} + +// Approach 4: Using a Map (preserves insertion order) +function removeDuplicatesMap(array: T[]): T[] { + const map = new Map(); + array.forEach(item => map.set(item, true)); + return Array.from(map.keys()); +} + +// Approach 5: Traditional loop approach +function removeDuplicatesLoop(array: T[]): T[] { + const unique: T[] = []; + for (let i = 0; i < array.length; i++) { + if (!unique.includes(array[i])) { + unique.push(array[i]); + } + } + return unique; +} + +// Approach 6: Remove duplicates from array of objects (by property) +function removeDuplicatesByProperty>( + array: T[], + property: keyof T +): T[] { + const seen = new Set(); + return array.filter(item => { + const value = item[property]; + if (seen.has(value)) { + return false; + } + seen.add(value); + return true; + }); +} + +// Examples +console.log('=== Remove Duplicates Examples ===\n'); + +const numbers: number[] = [1, 2, 3, 2, 4, 3, 5, 1, 6, 4]; +console.log('Original array:', numbers); + +console.log('\nUsing Set:', removeDuplicatesSet(numbers)); +// Output: [1, 2, 3, 4, 5, 6] + +console.log('Using filter:', removeDuplicatesFilter(numbers)); +// Output: [1, 2, 3, 4, 5, 6] + +console.log('Using reduce:', removeDuplicatesReduce(numbers)); +// Output: [1, 2, 3, 4, 5, 6] + +console.log('Using Map:', removeDuplicatesMap(numbers)); +// Output: [1, 2, 3, 4, 5, 6] + +console.log('Using loop:', removeDuplicatesLoop(numbers)); +// Output: [1, 2, 3, 4, 5, 6] + +// String array example +console.log('\n=== String Array Example ==='); +const fruits: string[] = ['apple', 'banana', 'apple', 'orange', 'banana', 'grape', 'apple']; +console.log('Original:', fruits); +console.log('Unique:', removeDuplicatesSet(fruits)); +// Output: ['apple', 'banana', 'orange', 'grape'] + +// Real-world example: Remove duplicate objects by property +console.log('\n=== Remove Duplicates from Objects ==='); + +interface User { + id: number; + name: string; + email: string; +} + +const users: User[] = [ + { id: 1, name: 'Alice', email: 'alice@example.com' }, + { id: 2, name: 'Bob', email: 'bob@example.com' }, + { id: 1, name: 'Alice Smith', email: 'alice@example.com' }, // duplicate id + { id: 3, name: 'Charlie', email: 'charlie@example.com' }, + { id: 2, name: 'Bob Johnson', email: 'bob@example.com' } // duplicate id +]; + +console.log('Original users:', users.length); +const uniqueUsersById = removeDuplicatesByProperty(users, 'id'); +console.log('Unique users by id:', uniqueUsersById.length); +console.log(uniqueUsersById); + +const uniqueUsersByEmail = removeDuplicatesByProperty(users, 'email'); +console.log('\nUnique users by email:', uniqueUsersByEmail.length); +console.log(uniqueUsersByEmail); + +// Performance comparison +console.log('\n=== Performance Comparison ==='); + +const largeArray: number[] = Array.from({ length: 10000 }, () => Math.floor(Math.random() * 1000)); + +console.time('Set method'); +removeDuplicatesSet(largeArray); +console.timeEnd('Set method'); + +console.time('Filter method'); +removeDuplicatesFilter(largeArray); +console.timeEnd('Filter method'); + +console.time('Reduce method'); +removeDuplicatesReduce(largeArray); +console.timeEnd('Reduce method'); + +console.time('Map method'); +removeDuplicatesMap(largeArray); +console.timeEnd('Map method'); + +console.time('Loop method'); +removeDuplicatesLoop(largeArray); +console.timeEnd('Loop method'); + +// Edge cases +console.log('\n=== Edge Cases ==='); +console.log('Empty array:', removeDuplicatesSet([])); +console.log('No duplicates:', removeDuplicatesSet([1, 2, 3, 4])); +console.log('All duplicates:', removeDuplicatesSet([5, 5, 5, 5])); +console.log('Mixed types:', removeDuplicatesSet([1, '1', 2, '2', true, 'true'])); +console.log('With null/undefined:', removeDuplicatesSet([1, null, 2, undefined, null, 3, undefined])); + +// Count duplicates +console.log('\n=== Count Duplicates ==='); +function countDuplicates(array: (string | number)[]): Record { + const counts: Record = {}; + array.forEach(item => { + counts[item] = (counts[item] || 0) + 1; + }); + return counts; +} + +const testArray: number[] = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]; +console.log('Array:', testArray); +console.log('Duplicate counts:', countDuplicates(testArray)); + +// to see the output of this file use the command: node src/03-array/13-remove-duplicates.ts diff --git a/src/03-array/14-array-rotation.js b/src/03-array/14-array-rotation.js new file mode 100644 index 00000000..e12909f0 --- /dev/null +++ b/src/03-array/14-array-rotation.js @@ -0,0 +1,186 @@ +// src/03-array/14-array-rotation.js + +/** + * Array Rotation - Rotate array elements left or right by k positions + * Common use case: Circular buffers, image processing, game development + */ + +// Approach 1: Rotate Right using slice and concat +function rotateRight(array, k) { + if (array.length === 0) return array; + + // Normalize k to handle cases where k > array.length + k = k % array.length; + + if (k === 0) return array; + + return array.slice(-k).concat(array.slice(0, -k)); +} + +// Approach 2: Rotate Left using slice and concat +function rotateLeft(array, k) { + if (array.length === 0) return array; + + k = k % array.length; + + if (k === 0) return array; + + return array.slice(k).concat(array.slice(0, k)); +} + +// Approach 3: Rotate Right using spread operator +function rotateRightSpread(array, k) { + if (array.length === 0) return array; + + k = k % array.length; + + if (k === 0) return [...array]; + + return [...array.slice(-k), ...array.slice(0, -k)]; +} + +// Approach 4: In-place rotation using reverse (most efficient) +function rotateRightInPlace(array, k) { + const arr = [...array]; // Create copy to avoid modifying original + const n = arr.length; + + if (n === 0) return arr; + + k = k % n; + + if (k === 0) return arr; + + // Reverse helper function + const reverse = (start, end) => { + while (start < end) { + [arr[start], arr[end]] = [arr[end], arr[start]]; + start++; + end--; + } + }; + + // Three-step reversal algorithm + reverse(0, n - 1); // Reverse entire array + reverse(0, k - 1); // Reverse first k elements + reverse(k, n - 1); // Reverse remaining elements + + return arr; +} + +// Approach 5: Rotate using unshift and pop (simple but less efficient) +function rotateRightSimple(array, k) { + const arr = [...array]; + k = k % arr.length; + + for (let i = 0; i < k; i++) { + arr.unshift(arr.pop()); + } + + return arr; +} + +// Examples +console.log('=== Array Rotation Examples ===\n'); + +const numbers = [1, 2, 3, 4, 5, 6, 7]; +console.log('Original array:', numbers); + +console.log('\n--- Rotate Right ---'); +console.log('Rotate right by 1:', rotateRight(numbers, 1)); +// Output: [7, 1, 2, 3, 4, 5, 6] + +console.log('Rotate right by 2:', rotateRight(numbers, 2)); +// Output: [6, 7, 1, 2, 3, 4, 5] + +console.log('Rotate right by 3:', rotateRight(numbers, 3)); +// Output: [5, 6, 7, 1, 2, 3, 4] + +console.log('\n--- Rotate Left ---'); +console.log('Rotate left by 1:', rotateLeft(numbers, 1)); +// Output: [2, 3, 4, 5, 6, 7, 1] + +console.log('Rotate left by 2:', rotateLeft(numbers, 2)); +// Output: [3, 4, 5, 6, 7, 1, 2] + +console.log('Rotate left by 3:', rotateLeft(numbers, 3)); +// Output: [4, 5, 6, 7, 1, 2, 3] + +// Edge cases +console.log('\n=== Edge Cases ==='); +console.log('Rotate by 0:', rotateRight(numbers, 0)); +console.log('Rotate by array length:', rotateRight(numbers, 7)); +console.log('Rotate by more than length:', rotateRight(numbers, 10)); +// 10 % 7 = 3, so same as rotating by 3 +console.log('Empty array:', rotateRight([], 3)); +console.log('Single element:', rotateRight([1], 5)); + +// Real-world example: Circular carousel +console.log('\n=== Real-World Example: Image Carousel ==='); + +const images = ['img1.jpg', 'img2.jpg', 'img3.jpg', 'img4.jpg', 'img5.jpg']; +console.log('Initial images:', images); + +console.log('\nNavigate next (rotate left by 1):'); +console.log(rotateLeft(images, 1)); + +console.log('\nNavigate previous (rotate right by 1):'); +console.log(rotateRight(images, 1)); + +console.log('\nJump 2 images forward:'); +console.log(rotateLeft(images, 2)); + +// Real-world example: Shift scheduling +console.log('\n=== Real-World Example: Shift Rotation ==='); + +const employees = ['Alice', 'Bob', 'Charlie', 'David', 'Eve']; +console.log('Week 1 schedule:', employees); +console.log('Week 2 schedule (rotate by 1):', rotateLeft(employees, 1)); +console.log('Week 3 schedule (rotate by 2):', rotateLeft(employees, 2)); +console.log('Week 4 schedule (rotate by 3):', rotateLeft(employees, 3)); + +// Performance comparison +console.log('\n=== Performance Comparison ==='); + +const largeArray = Array.from({ length: 10000 }, (_, i) => i); +const k = 3000; + +console.time('Slice method'); +rotateRight(largeArray, k); +console.timeEnd('Slice method'); + +console.time('Spread method'); +rotateRightSpread(largeArray, k); +console.timeEnd('Spread method'); + +console.time('In-place reversal'); +rotateRightInPlace(largeArray, k); +console.timeEnd('In-place reversal'); + +console.time('Simple method'); +rotateRightSimple(largeArray, k); +console.timeEnd('Simple method'); + +// Utility: Find rotation count +console.log('\n=== Find Rotation Count ==='); + +function findRotationCount(sortedRotatedArray) { + // Find the index of minimum element in a rotated sorted array + let minIndex = 0; + let minValue = sortedRotatedArray[0]; + + for (let i = 1; i < sortedRotatedArray.length; i++) { + if (sortedRotatedArray[i] < minValue) { + minValue = sortedRotatedArray[i]; + minIndex = i; + } + } + + return minIndex; +} + +const rotatedSorted = [4, 5, 6, 7, 1, 2, 3]; +console.log('Rotated sorted array:', rotatedSorted); +console.log('Number of rotations:', findRotationCount(rotatedSorted)); +// Output: 4 (array was rotated 4 times to the left from [1,2,3,4,5,6,7]) + +// to see the output of this file use the command: node src/03-array/14-array-rotation.js diff --git a/src/03-array/14-array-rotation.ts b/src/03-array/14-array-rotation.ts new file mode 100644 index 00000000..8bf6ef44 --- /dev/null +++ b/src/03-array/14-array-rotation.ts @@ -0,0 +1,189 @@ +// src/03-array/14-array-rotation.ts + +/** + * Array Rotation - Rotate array elements left or right by k positions + * Common use case: Circular buffers, image processing, game development + */ + +// Approach 1: Rotate Right using slice and concat +function rotateRight(array: T[], k: number): T[] { + if (array.length === 0) return array; + + // Normalize k to handle cases where k > array.length + k = k % array.length; + + if (k === 0) return array; + + return array.slice(-k).concat(array.slice(0, -k)); +} + +// Approach 2: Rotate Left using slice and concat +function rotateLeft(array: T[], k: number): T[] { + if (array.length === 0) return array; + + k = k % array.length; + + if (k === 0) return array; + + return array.slice(k).concat(array.slice(0, k)); +} + +// Approach 3: Rotate Right using spread operator +function rotateRightSpread(array: T[], k: number): T[] { + if (array.length === 0) return array; + + k = k % array.length; + + if (k === 0) return [...array]; + + return [...array.slice(-k), ...array.slice(0, -k)]; +} + +// Approach 4: In-place rotation using reverse (most efficient) +function rotateRightInPlace(array: T[], k: number): T[] { + const arr = [...array]; // Create copy to avoid modifying original + const n = arr.length; + + if (n === 0) return arr; + + k = k % n; + + if (k === 0) return arr; + + // Reverse helper function + const reverse = (start: number, end: number): void => { + while (start < end) { + [arr[start], arr[end]] = [arr[end], arr[start]]; + start++; + end--; + } + }; + + // Three-step reversal algorithm + reverse(0, n - 1); // Reverse entire array + reverse(0, k - 1); // Reverse first k elements + reverse(k, n - 1); // Reverse remaining elements + + return arr; +} + +// Approach 5: Rotate using unshift and pop (simple but less efficient) +function rotateRightSimple(array: T[], k: number): T[] { + const arr = [...array]; + k = k % arr.length; + + for (let i = 0; i < k; i++) { + const last = arr.pop(); + if (last !== undefined) { + arr.unshift(last); + } + } + + return arr; +} + +// Examples +console.log('=== Array Rotation Examples ===\n'); + +const numbers: number[] = [1, 2, 3, 4, 5, 6, 7]; +console.log('Original array:', numbers); + +console.log('\n--- Rotate Right ---'); +console.log('Rotate right by 1:', rotateRight(numbers, 1)); +// Output: [7, 1, 2, 3, 4, 5, 6] + +console.log('Rotate right by 2:', rotateRight(numbers, 2)); +// Output: [6, 7, 1, 2, 3, 4, 5] + +console.log('Rotate right by 3:', rotateRight(numbers, 3)); +// Output: [5, 6, 7, 1, 2, 3, 4] + +console.log('\n--- Rotate Left ---'); +console.log('Rotate left by 1:', rotateLeft(numbers, 1)); +// Output: [2, 3, 4, 5, 6, 7, 1] + +console.log('Rotate left by 2:', rotateLeft(numbers, 2)); +// Output: [3, 4, 5, 6, 7, 1, 2] + +console.log('Rotate left by 3:', rotateLeft(numbers, 3)); +// Output: [4, 5, 6, 7, 1, 2, 3] + +// Edge cases +console.log('\n=== Edge Cases ==='); +console.log('Rotate by 0:', rotateRight(numbers, 0)); +console.log('Rotate by array length:', rotateRight(numbers, 7)); +console.log('Rotate by more than length:', rotateRight(numbers, 10)); +// 10 % 7 = 3, so same as rotating by 3 +console.log('Empty array:', rotateRight([], 3)); +console.log('Single element:', rotateRight([1], 5)); + +// Real-world example: Circular carousel +console.log('\n=== Real-World Example: Image Carousel ==='); + +const images: string[] = ['img1.jpg', 'img2.jpg', 'img3.jpg', 'img4.jpg', 'img5.jpg']; +console.log('Initial images:', images); + +console.log('\nNavigate next (rotate left by 1):'); +console.log(rotateLeft(images, 1)); + +console.log('\nNavigate previous (rotate right by 1):'); +console.log(rotateRight(images, 1)); + +console.log('\nJump 2 images forward:'); +console.log(rotateLeft(images, 2)); + +// Real-world example: Shift scheduling +console.log('\n=== Real-World Example: Shift Rotation ==='); + +const employees: string[] = ['Alice', 'Bob', 'Charlie', 'David', 'Eve']; +console.log('Week 1 schedule:', employees); +console.log('Week 2 schedule (rotate by 1):', rotateLeft(employees, 1)); +console.log('Week 3 schedule (rotate by 2):', rotateLeft(employees, 2)); +console.log('Week 4 schedule (rotate by 3):', rotateLeft(employees, 3)); + +// Performance comparison +console.log('\n=== Performance Comparison ==='); + +const largeArray: number[] = Array.from({ length: 10000 }, (_, i) => i); +const k = 3000; + +console.time('Slice method'); +rotateRight(largeArray, k); +console.timeEnd('Slice method'); + +console.time('Spread method'); +rotateRightSpread(largeArray, k); +console.timeEnd('Spread method'); + +console.time('In-place reversal'); +rotateRightInPlace(largeArray, k); +console.timeEnd('In-place reversal'); + +console.time('Simple method'); +rotateRightSimple(largeArray, k); +console.timeEnd('Simple method'); + +// Utility: Find rotation count +console.log('\n=== Find Rotation Count ==='); + +function findRotationCount(sortedRotatedArray: number[]): number { + // Find the index of minimum element in a rotated sorted array + let minIndex = 0; + let minValue = sortedRotatedArray[0]; + + for (let i = 1; i < sortedRotatedArray.length; i++) { + if (sortedRotatedArray[i] < minValue) { + minValue = sortedRotatedArray[i]; + minIndex = i; + } + } + + return minIndex; +} + +const rotatedSorted: number[] = [4, 5, 6, 7, 1, 2, 3]; +console.log('Rotated sorted array:', rotatedSorted); +console.log('Number of rotations:', findRotationCount(rotatedSorted)); +// Output: 4 (array was rotated 4 times to the left from [1,2,3,4,5,6,7]) + +// to see the output of this file use the command: node src/03-array/14-array-rotation.ts