Master the spread (...) operator for arrays and objects, and rest parameters for flexible function arguments.
The spread (...) operator is one of ES6's most versatile features, serving dual purposes as both a spread operator and rest parameter collector. This seemingly simple syntax revolutionizes how we work with arrays, objects, and function parameters, making code more concise and expressive.
Whether you're cloning arrays, merging objects, passing dynamic arguments to functions, or collecting variable parameters, the spread operator provides elegant solutions to common programming challenges. It's essential for modern JavaScript development, especially in React where immutable updates and prop spreading are fundamental patterns.
In this comprehensive lesson, you'll master both spread and rest syntax, understand their differences and applications, and learn to leverage these operators for cleaner, more maintainable code.
The spread operator expands arrays into individual elements, enabling powerful array manipulation patterns that replace verbose traditional methods.
1. Array Expansion and Concatenation
1// Traditional array concatenation
2const arr1 = [1, 2, 3];
3const arr2 = [4, 5, 6];
4const combined = arr1.concat(arr2); // [1, 2, 3, 4, 5, 6]
5
6// Spread operator - much cleaner
7const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
8
9// Insert elements at any position
10const middle = [7, 8];
11const result = [...arr1, ...middle, ...arr2]; // [1, 2, 3, 7, 8, 4, 5, 6]
12
13// Add elements while spreading
14const extended = [0, ...arr1, 3.5, ...arr2, 7]; // [0, 1, 2, 3, 3.5, 4, 5, 6, 7]2. Array Cloning and Shallow Copying
1const original = [1, 2, 3, { name: 'Alice' }];
2
3// Traditional cloning
4const clone1 = original.slice(); // Shallow copy
5const clone2 = Array.from(original); // Shallow copy
6
7// Spread operator cloning - more intuitive
8const clone3 = [...original]; // Shallow copy
9
10// They're different arrays but share object references
11console.log(clone3 === original); // false
12console.log(clone3[3] === original[3]); // true (same object reference)
13
14// Creating arrays from iterables
15const str = 'hello';
16const chars = [...str]; // ['h', 'e', 'l', 'l', 'o']
17
18const nodeList = document.querySelectorAll('div');
19const divArray = [...nodeList]; // Convert NodeList to Array3. Array Manipulation Patterns
1// Remove duplicates (with Set)
2const numbers = [1, 2, 2, 3, 3, 3, 4, 5, 5];
3const unique = [...new Set(numbers)]; // [1, 2, 3, 4, 5]
4
5// Find min/max values
6const scores = [85, 92, 78, 96, 88, 94];
7const highest = Math.max(...scores); // 96
8const lowest = Math.min(...scores); // 78
9
10// Insert element at specific position
11const fruits = ['apple', 'banana', 'orange'];
12const insertAt = 1;
13const newFruit = 'grape';
14const updated = [
15 ...fruits.slice(0, insertAt),
16 newFruit,
17 ...fruits.slice(insertAt)
18]; // ['apple', 'grape', 'banana', 'orange']
19
20// Replace element immutably
21const replace = (arr, index, newValue) => [
22 ...arr.slice(0, index),
23 newValue,
24 ...arr.slice(index + 1)
25];
26
27const colors = ['red', 'green', 'blue'];
28const updated = replace(colors, 1, 'yellow'); // ['red', 'yellow', 'blue']4. Functional Programming Patterns
1// Flatten arrays
2const nested = [[1, 2], [3, 4], [5, 6]];
3const flattened = [].concat(...nested); // [1, 2, 3, 4, 5, 6]
4// Or with flat() method (modern approach)
5const flattened2 = nested.flat(); // [1, 2, 3, 4, 5, 6]
6
7// Conditional array spreading
8const includeExtras = true;
9const baseItems = ['item1', 'item2'];
10const extras = ['extra1', 'extra2'];
11const result = [
12 ...baseItems,
13 ...(includeExtras ? extras : [])
14]; // ['item1', 'item2', 'extra1', 'extra2']
15
16// Array shuffling
17const shuffle = (arr) => {
18 const shuffled = [...arr];
19 for (let i = shuffled.length - 1; i > 0; i--) {
20 const j = Math.floor(Math.random() * (i + 1));
21 [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
22 }
23 return shuffled;
24};
25
26const deck = ['A', 'K', 'Q', 'J'];
27const shuffledDeck = shuffle(deck); // Random order, original unchangedBest Practices for Array Spreading:
Object spreading enables clean object cloning, merging, and property updates without mutation, essential for immutable programming patterns.
1. Object Cloning and Merging
1const user = {
2 name: 'Alice Johnson',
3 age: 28,
4 email: '[email protected]'
5};
6
7// Shallow clone
8const userCopy = { ...user };
9console.log(userCopy === user); // false (different objects)
10console.log(userCopy.name === user.name); // true (same primitive values)
11
12// Merge objects
13const defaults = {
14 theme: 'light',
15 notifications: true,
16 language: 'en'
17};
18
19const userPrefs = {
20 theme: 'dark',
21 fontSize: 16
22};
23
24// Later properties override earlier ones
25const settings = { ...defaults, ...userPrefs };
26// Result: { theme: 'dark', notifications: true, language: 'en', fontSize: 16 }
27
28// Order matters for property precedence
29const settings2 = { ...userPrefs, ...defaults };
30// Result: { theme: 'light', fontSize: 16, notifications: true, language: 'en' }2. Immutable Object Updates
1const person = {
2 name: 'Bob Smith',
3 age: 25,
4 address: {
5 street: '123 Main St',
6 city: 'New York',
7 zip: '10001'
8 },
9 hobbies: ['reading', 'coding']
10};
11
12// Update properties immutably
13const updatedPerson = {
14 ...person,
15 age: 26,
16 email: '[email protected]' // Add new property
17};
18
19// Nested object updates require nested spreading
20const relocatedPerson = {
21 ...person,
22 address: {
23 ...person.address,
24 city: 'San Francisco',
25 zip: '94102'
26 }
27};
28
29// Conditional property addition
30const includeEmail = true;
31const personWithEmail = {
32 ...person,
33 ...(includeEmail && { email: '[email protected]' })
34};
35
36// Remove properties (using destructuring + rest)
37const { address, ...personWithoutAddress } = person;
38console.log(personWithoutAddress); // person without address property3. Dynamic Object Building
1// Computed property names with spread
2const createUser = (name, role, ...permissions) => ({
3 id: Date.now(),
4 name,
5 role,
6 permissions,
7 [`is${role}`]: true, // Computed property
8 createdAt: new Date().toISOString()
9});
10
11const admin = createUser('Alice', 'Admin', 'read', 'write', 'delete');
12// { id: 1640000000000, name: 'Alice', role: 'Admin',
13// permissions: ['read', 'write', 'delete'], isAdmin: true, createdAt: '...' }
14
15// Conditional object spreading
16const buildConfig = ({ env, debug = false, ...options }) => ({
17 environment: env,
18 ...(debug && {
19 debugMode: true,
20 logLevel: 'verbose',
21 sourceMap: true
22 }),
23 ...options,
24 timestamp: Date.now()
25});
26
27const config = buildConfig({
28 env: 'development',
29 debug: true,
30 port: 3000,
31 cors: true
32});4. Advanced Object Patterns
1// Object transformation pipeline
2const transformUser = (user) => ({
3 ...user,
4 name: user.name.toUpperCase(),
5 initials: user.name.split(' ').map(n => n[0]).join(''),
6 displayName: `${user.name} (${user.email})`,
7 metadata: {
8 lastUpdated: new Date().toISOString(),
9 version: 1
10 }
11});
12
13// Merge multiple objects with conflict resolution
14const mergeWithPriority = (...objects) => {
15 return objects.reduce((result, obj) => ({
16 ...result,
17 ...obj
18 }), {});
19};
20
21const base = { a: 1, b: 2 };
22const override1 = { b: 3, c: 4 };
23const override2 = { c: 5, d: 6 };
24const merged = mergeWithPriority(base, override1, override2);
25// { a: 1, b: 3, c: 5, d: 6 }
26
27// Object filtering with spread
28const filterObject = (obj, predicate) => {
29 return Object.keys(obj)
30 .filter(key => predicate(obj[key], key))
31 .reduce((result, key) => ({
32 ...result,
33 [key]: obj[key]
34 }), {});
35};
36
37const data = { a: 1, b: 2, c: 3, d: 4 };
38const evens = filterObject(data, value => value % 2 === 0);
39// { b: 2, d: 4 }
40
41// State management patterns (Redux-style)
42const updateNestedState = (state, path, value) => {
43 const [head, ...rest] = path;
44
45 if (rest.length === 0) {
46 return { ...state, [head]: value };
47 }
48
49 return {
50 ...state,
51 [head]: updateNestedState(state[head], rest, value)
52 };
53};
54
55const appState = {
56 user: {
57 profile: { name: 'Alice', theme: 'light' },
58 settings: { notifications: true }
59 }
60};
61
62const newState = updateNestedState(appState, ['user', 'profile', 'theme'], 'dark');
63// Deep update without mutationObject Spreading Best Practices:
Rest parameters collect function arguments into an array, enabling flexible function signatures and elegant handling of variable argument counts.
1. Basic Rest Parameters
1// Traditional approach with arguments object
2function oldSum() {
3 let total = 0;
4 for (let i = 0; i < arguments.length; i++) {
5 total += arguments[i]; // arguments is array-like, not a real array
6 }
7 return total;
8}
9
10// Modern approach with rest parameters
11function sum(...numbers) {
12 // numbers is a real array with all array methods
13 return numbers.reduce((total, num) => total + num, 0);
14}
15
16console.log(sum(1, 2, 3, 4, 5)); // 15
17console.log(sum(10, 20)); // 30
18console.log(sum()); // 0
19
20// Rest with named parameters
21function greetUsers(greeting, ...names) {
22 return `${greeting} ${names.join(', ')}!`;
23}
24
25console.log(greetUsers('Hello', 'Alice', 'Bob', 'Charlie'));
26// "Hello Alice, Bob, Charlie!"2. Advanced Rest Parameter Patterns
1// Flexible configuration functions
2function createServer(port, host = 'localhost', ...middleware) {
3 console.log(`Server on ${host}:${port}`);
4 console.log(`Middleware: ${middleware.join(', ')}`);
5
6 return {
7 start: () => console.log('Server started'),
8 use: (mw) => middleware.push(mw),
9 middleware
10 };
11}
12
13const server = createServer(3000, 'localhost', 'cors', 'helmet', 'compression');
14
15// Logger function with multiple log levels
16function log(level, message, ...details) {
17 const timestamp = new Date().toISOString();
18 console.log(`[${timestamp}] ${level.toUpperCase()}: ${message}`);
19
20 if (details.length > 0) {
21 console.log('Details:', details);
22 }
23}
24
25log('info', 'User logged in', { userId: 123 }, { timestamp: Date.now() });
26log('error', 'Database connection failed');
27
28// Mathematical operations
29function calculate(operation, ...operands) {
30 switch (operation) {
31 case 'add':
32 return operands.reduce((sum, num) => sum + num, 0);
33 case 'multiply':
34 return operands.reduce((product, num) => product * num, 1);
35 case 'max':
36 return Math.max(...operands);
37 case 'min':
38 return Math.min(...operands);
39 default:
40 throw new Error(`Unknown operation: ${operation}`);
41 }
42}
43
44console.log(calculate('add', 1, 2, 3, 4)); // 10
45console.log(calculate('multiply', 2, 3, 4)); // 24
46console.log(calculate('max', 5, 2, 8, 1)); // 83. Rest with Destructuring
1// Array destructuring with rest
2function processNumbers([first, second, ...rest]) {
3 return {
4 first,
5 second,
6 remaining: rest,
7 total: first + second + rest.reduce((sum, num) => sum + num, 0),
8 count: rest.length + 2
9 };
10}
11
12const result = processNumbers([10, 20, 30, 40, 50]);
13// { first: 10, second: 20, remaining: [30, 40, 50], total: 150, count: 5 }
14
15// Object destructuring with rest
16function updateUser({ id, name, email, ...otherData }) {
17 console.log(`Updating user ${id}: ${name} (${email})`);
18 console.log('Additional data:', otherData);
19
20 return {
21 id,
22 name,
23 email,
24 ...otherData,
25 lastUpdated: new Date().toISOString()
26 };
27}
28
29const userData = {
30 id: 123,
31 name: 'Alice Johnson',
32 email: '[email protected]',
33 age: 28,
34 city: 'New York',
35 preferences: { theme: 'dark' }
36};
37
38const updated = updateUser(userData);
39
40// Function composition with rest
41function compose(...functions) {
42 return (value) => functions.reduceRight((acc, fn) => fn(acc), value);
43}
44
45const addOne = x => x + 1;
46const double = x => x * 2;
47const square = x => x * x;
48
49const pipeline = compose(square, double, addOne);
50console.log(pipeline(3)); // ((3 + 1) * 2)² = 644. Real-World Applications
1// Event emitter pattern
2class EventEmitter {
3 constructor() {
4 this.events = {};
5 }
6
7 on(event, listener) {
8 if (!this.events[event]) {
9 this.events[event] = [];
10 }
11 this.events[event].push(listener);
12 }
13
14 emit(event, ...args) {
15 if (this.events[event]) {
16 this.events[event].forEach(listener => listener(...args));
17 }
18 }
19}
20
21const emitter = new EventEmitter();
22emitter.on('user-action', (action, userId, ...metadata) => {
23 console.log(`User ${userId} performed ${action}`, metadata);
24});
25
26emitter.emit('user-action', 'login', 123, { ip: '192.168.1.1' }, { device: 'mobile' });
27
28// API request builder
29function apiRequest(method, url, ...args) {
30 const [data, options = {}] = args;
31
32 const config = {
33 method: method.toUpperCase(),
34 headers: {
35 'Content-Type': 'application/json',
36 ...options.headers
37 },
38 ...options
39 };
40
41 if (data && ['POST', 'PUT', 'PATCH'].includes(config.method)) {
42 config.body = JSON.stringify(data);
43 }
44
45 return fetch(url, config);
46}
47
48// Usage
49apiRequest('GET', '/api/users');
50apiRequest('POST', '/api/users', { name: 'Alice' });
51apiRequest('PUT', '/api/users/123', { name: 'Alice' }, {
52 headers: { Authorization: 'Bearer token' }
53});
54
55// Database query builder
56function buildQuery(table, ...clauses) {
57 let query = `SELECT * FROM ${table}`;
58
59 clauses.forEach(clause => {
60 if (clause.where) query += ` WHERE ${clause.where}`;
61 if (clause.orderBy) query += ` ORDER BY ${clause.orderBy}`;
62 if (clause.limit) query += ` LIMIT ${clause.limit}`;
63 });
64
65 return query;
66}
67
68const query = buildQuery(
69 'users',
70 { where: 'active = 1' },
71 { orderBy: 'created_at DESC' },
72 { limit: 10 }
73);
74// "SELECT * FROM users WHERE active = 1 ORDER BY created_at DESC LIMIT 10"Rest Parameters Best Practices:
Combining spread and rest operators with other ES6 features creates powerful patterns for data manipulation, function composition, and modern JavaScript development.
1. Function Call Spreading
1// Apply pattern replacement
2const numbers = [1, 2, 3, 4, 5];
3
4// Traditional apply usage
5const max = Math.max.apply(null, numbers);
6
7// Modern spread syntax
8const max = Math.max(...numbers);
9const min = Math.min(...numbers);
10const sum = numbers.reduce((a, b) => a + b);
11
12// Function that accepts multiple arrays
13function combineAndProcess(...arrays) {
14 const combined = [].concat(...arrays);
15 return {
16 total: combined.length,
17 unique: [...new Set(combined)],
18 sum: combined.reduce((sum, num) => sum + num, 0),
19 max: Math.max(...combined),
20 min: Math.min(...combined)
21 };
22}
23
24const result = combineAndProcess(
25 [1, 2, 3],
26 [4, 5, 6],
27 [3, 4, 7] // Has duplicates
28);
29// { total: 9, unique: [1, 2, 3, 4, 5, 6, 7], sum: 35, max: 7, min: 1 }
30
31// Date constructor with spread
32const dateComponents = [2024, 0, 15, 10, 30, 0]; // Year, month (0-based), day, hour, min, sec
33const date = new Date(...dateComponents);
34console.log(date); // Mon Jan 15 2024 10:30:002. Combining with Destructuring
1// Array head/tail pattern
2function processArray([head, ...tail]) {
3 return {
4 head,
5 tail,
6 processed: tail.map(x => x * head),
7 sum: head + tail.reduce((sum, x) => sum + x, 0)
8 };
9}
10
11const result = processArray([2, 3, 4, 5]);
12// { head: 2, tail: [3, 4, 5], processed: [6, 8, 10], sum: 14 }
13
14// Object property grouping
15function categorizeUser({ role, permissions, ...profile }) {
16 return {
17 authentication: { role, permissions },
18 profile,
19 hasAdminAccess: permissions.includes('admin'),
20 displayInfo: `${profile.name} (${role})`
21 };
22}
23
24const user = {
25 id: 123,
26 name: 'Alice Johnson',
27 email: '[email protected]',
28 role: 'moderator',
29 permissions: ['read', 'write', 'moderate'],
30 avatar: '/avatars/alice.jpg'
31};
32
33const categorized = categorizeUser(user);
34console.log(categorized.authentication); // { role: 'moderator', permissions: [...] }
35console.log(categorized.profile); // { id: 123, name: 'Alice Johnson', email: '...', avatar: '...' }
36
37// Nested destructuring with rest
38function processApiResponse({
39 data: { users, ...otherData },
40 meta: { page, totalPages, ...metaRest } = {},
41 ...responseRest
42}) {
43 return {
44 users: users || [],
45 pagination: { page, totalPages, ...metaRest },
46 additionalData: { ...otherData, ...responseRest }
47 };
48}3. Functional Programming Patterns
1// Curry function with rest parameters
2const curry = (fn) => (...args) => {
3 if (args.length >= fn.length) {
4 return fn(...args);
5 }
6 return (...nextArgs) => curry(fn)(...args, ...nextArgs);
7};
8
9const add = (a, b, c) => a + b + c;
10const curriedAdd = curry(add);
11
12console.log(curriedAdd(1)(2)(3)); // 6
13console.log(curriedAdd(1, 2)(3)); // 6
14console.log(curriedAdd(1, 2, 3)); // 6
15
16// Pipe function composition
17const pipe = (...functions) => (value) =>
18 functions.reduce((acc, fn) => fn(acc), value);
19
20const compose = (...functions) => (value) =>
21 functions.reduceRight((acc, fn) => fn(acc), value);
22
23// Transform user data pipeline
24const normalizeEmail = (user) => ({
25 ...user,
26 email: user.email.toLowerCase().trim()
27});
28
29const addTimestamp = (user) => ({
30 ...user,
31 processedAt: new Date().toISOString()
32});
33
34const addInitials = (user) => ({
35 ...user,
36 initials: user.name.split(' ').map(n => n[0]).join('')
37});
38
39const processUser = pipe(
40 normalizeEmail,
41 addInitials,
42 addTimestamp
43);
44
45const rawUser = { name: 'John Doe', email: ' [email protected] ' };
46const processed = processUser(rawUser);
47
48// Partial application with spread
49const partial = (fn, ...presetArgs) => (...laterArgs) =>
50 fn(...presetArgs, ...laterArgs);
51
52const multiply = (a, b, c) => a * b * c;
53const double = partial(multiply, 2);
54const quadruple = partial(multiply, 2, 2);
55
56console.log(double(3, 4)); // 2 * 3 * 4 = 24
57console.log(quadruple(5)); // 2 * 2 * 5 = 204. React/Framework Patterns
1// React props forwarding
2function BaseButton({ children, className = '', ...props }) {
3 return (
4 <button
5 className={`btn ${className}`}
6 {...props}
7 >
8 {children}
9 </button>
10 );
11}
12
13function PrimaryButton({ size = 'medium', ...props }) {
14 return (
15 <BaseButton
16 className={`btn-primary btn-${size}`}
17 {...props}
18 />
19 );
20}
21
22// Usage: <PrimaryButton onClick={handleClick} disabled={loading}>Submit</PrimaryButton>
23
24// State management patterns
25const useFormData = (initialData = {}) => {
26 const [data, setData] = useState(initialData);
27
28 const updateField = (field, value) => {
29 setData(prev => ({ ...prev, [field]: value }));
30 };
31
32 const updateFields = (updates) => {
33 setData(prev => ({ ...prev, ...updates }));
34 };
35
36 const reset = (newData = initialData) => {
37 setData({ ...newData });
38 };
39
40 return { data, updateField, updateFields, reset };
41};
42
43// Component with dynamic props
44function DynamicComponent({
45 component: Component = 'div',
46 children,
47 ...props
48}) {
49 return <Component {...props}>{children}</Component>;
50}
51
52// Usage:
53// <DynamicComponent component="button" onClick={handler}>Click me</DynamicComponent>
54// <DynamicComponent component={Link} to="/home">Home</DynamicComponent>
55
56// HOC pattern with props forwarding
57const withLoading = (WrappedComponent) => {
58 return function WithLoadingComponent({ loading, ...props }) {
59 if (loading) {
60 return <div className="spinner">Loading...</div>;
61 }
62 return <WrappedComponent {...props} />;
63 };
64};
65
66// API response spreading
67const processApiData = ({ data, errors, meta, ...response }) => ({
68 items: data || [],
69 hasErrors: errors && errors.length > 0,
70 pagination: meta?.pagination || {},
71 metadata: {
72 ...meta,
73 requestTime: response.timestamp,
74 status: response.status
75 }
76});5. Performance Considerations
1// Efficient object updates
2const updateUserImmutable = (user, updates) => {
3 // Only spread if there are actual updates
4 return Object.keys(updates).length > 0
5 ? { ...user, ...updates }
6 : user;
7};
8
9// Conditional spreading to avoid unnecessary operations
10const buildConfig = (baseConfig, isDevelopment, customConfig = {}) => ({
11 ...baseConfig,
12 ...(isDevelopment && {
13 debug: true,
14 sourceMap: true,
15 hotReload: true
16 }),
17 ...customConfig
18});
19
20// Memoized spread operations
21const memoizedMerge = (() => {
22 const cache = new WeakMap();
23
24 return (base, ...objects) => {
25 const key = objects;
26 if (cache.has(base) && cache.get(base).has(key)) {
27 return cache.get(base).get(key);
28 }
29
30 const result = { ...base, ...Object.assign({}, ...objects) };
31
32 if (!cache.has(base)) cache.set(base, new WeakMap());
33 cache.get(base).set(key, result);
34
35 return result;
36 };
37})();
38
39// Large array operations
40const processLargeArray = (largeArray, batchSize = 1000) => {
41 const results = [];
42
43 for (let i = 0; i < largeArray.length; i += batchSize) {
44 const batch = largeArray.slice(i, i + batchSize);
45 const processed = [...batch.map(item => ({ ...item, processed: true }))];
46 results.push(...processed);
47 }
48
49 return results;
50};Advanced Pattern Best Practices:
Create a comprehensive data processing library using spread and rest operators for array/object manipulation, function composition, and flexible APIs.
1// Modern Data Processing Utility with Spread & Rest Operators
2class DataProcessor {
3 constructor(...processors) {
4 this.processors = processors;
5 this.cache = new Map();
6 this.stats = { processed: 0, cached: 0, errors: 0 };
7 }
8
9 // Array processing with spread/rest patterns
10 processArrays(...arrays) {
11 const combined = [].concat(...arrays);
12 const unique = [...new Set(combined)];
13
14 return {
15 original: arrays,
16 combined,
17 unique,
18 stats: {
19 totalArrays: arrays.length,
20 totalItems: combined.length,
21 uniqueItems: unique.length,
22 duplicates: combined.length - unique.length
23 }
24 };
25 }
26
27 // Object merging with deep spread
28 mergeObjects(base = {}, ...objects) {
29 const cacheKey = JSON.stringify({ base, objects });
30
31 if (this.cache.has(cacheKey)) {
32 this.stats.cached++;
33 return this.cache.get(cacheKey);
34 }
35
36 try {
37 const merged = objects.reduce((result, obj) => {
38 return this.deepMerge(result, obj);
39 }, { ...base });
40
41 this.cache.set(cacheKey, merged);
42 this.stats.processed++;
43
44 return merged;
45 } catch (error) {
46 this.stats.errors++;
47 throw error;
48 }
49 }
50
51 // Deep merge utility
52 deepMerge(target, source) {
53 const result = { ...target };
54
55 for (const [key, value] of Object.entries(source)) {
56 if (value && typeof value === 'object' && !Array.isArray(value)) {
57 result[key] = this.deepMerge(result[key] || {}, value);
58 } else if (Array.isArray(value)) {
59 result[key] = [...(result[key] || []), ...value];
60 } else {
61 result[key] = value;
62 }
63 }
64
65 return result;
66 }
67
68 // Flexible transformation pipeline
69 transform(data, ...transformers) {
70 return transformers.reduce((current, transformer) => {
71 if (typeof transformer === 'function') {
72 return transformer(current);
73 }
74
75 if (transformer.type === 'filter') {
76 return Array.isArray(current)
77 ? current.filter(transformer.predicate)
78 : current;
79 }
80
81 if (transformer.type === 'map') {
82 return Array.isArray(current)
83 ? current.map(transformer.mapper)
84 : transformer.mapper(current);
85 }
86
87 return current;
88 }, data);
89 }
90
91 // Batch processing with rest parameters
92 processBatch(batchSize, processor, ...items) {
93 const results = [];
94
95 for (let i = 0; i < items.length; i += batchSize) {
96 const batch = items.slice(i, i + batchSize);
97 const processed = processor(...batch);
98 results.push(...(Array.isArray(processed) ? processed : [processed]));
99 }
100
101 return results;
102 }
103
104 // Function composition utilities
105 pipe(...functions) {
106 return (value) => functions.reduce((acc, fn) => fn(acc), value);
107 }
108
109 compose(...functions) {
110 return (value) => functions.reduceRight((acc, fn) => fn(acc), value);
111 }
112
113 // Dynamic configuration builder
114 buildConfig(base = {}, environment, features = [], ...overrides) {
115 const config = { ...base };
116
117 // Environment-specific settings
118 const envSettings = {
119 development: {
120 debug: true,
121 logging: 'verbose',
122 hotReload: true
123 },
124 production: {
125 debug: false,
126 logging: 'error',
127 optimization: true,
128 minify: true
129 },
130 test: {
131 debug: true,
132 logging: 'silent',
133 mockApi: true
134 }
135 };
136
137 // Feature flags
138 const featureSettings = features.reduce((acc, feature) => {
139 const featureConfig = {
140 analytics: { trackingEnabled: true, anonymousId: true },
141 social: { shareButtons: true, comments: true },
142 payments: { stripeEnabled: true, paypalEnabled: true },
143 notifications: { email: true, push: true, sms: false }
144 };
145
146 return { ...acc, ...featureConfig[feature] };
147 }, {});
148
149 // Merge all configurations
150 return this.mergeObjects(
151 config,
152 envSettings[environment] || {},
153 featureSettings,
154 ...overrides
155 );
156 }
157
158 // Advanced array manipulation
159 arrayUtils = {
160 // Zip arrays together
161 zip: (...arrays) => {
162 const maxLength = Math.max(...arrays.map(arr => arr.length));
163 return Array.from({ length: maxLength }, (_, i) =>
164 arrays.map(arr => arr[i])
165 );
166 },
167
168 // Chunk array into smaller arrays
169 chunk: (array, size) => {
170 const chunks = [];
171 for (let i = 0; i < array.length; i += size) {
172 chunks.push(array.slice(i, i + size));
173 }
174 return chunks;
175 },
176
177 // Flatten nested arrays
178 flatten: (array, depth = Infinity) => {
179 return depth > 0
180 ? array.reduce((acc, val) =>
181 acc.concat(Array.isArray(val)
182 ? this.arrayUtils.flatten(val, depth - 1)
183 : val
184 ), [])
185 : [...array];
186 },
187
188 // Remove items by predicate
189 remove: (array, predicate) =>
190 array.filter(item => !predicate(item)),
191
192 // Update items immutably
193 update: (array, predicate, updater) =>
194 array.map(item => predicate(item) ? { ...item, ...updater(item) } : item),
195
196 // Group by key
197 groupBy: (array, key) =>
198 array.reduce((groups, item) => {
199 const group = typeof key === 'function' ? key(item) : item[key];
200 return { ...groups, [group]: [...(groups[group] || []), item] };
201 }, {})
202 };
203
204 // Object utilities
205 objectUtils = {
206 // Pick specific properties
207 pick: (obj, ...keys) =>
208 keys.reduce((result, key) => {
209 if (key in obj) result[key] = obj[key];
210 return result;
211 }, {}),
212
213 // Omit specific properties
214 omit: (obj, ...keys) => {
215 const { [keys[0]]: omitted, ...rest } = obj;
216 return keys.length > 1
217 ? this.objectUtils.omit(rest, ...keys.slice(1))
218 : rest;
219 },
220
221 // Transform object values
222 mapValues: (obj, mapper) =>
223 Object.keys(obj).reduce((result, key) => ({
224 ...result,
225 [key]: mapper(obj[key], key)
226 }), {}),
227
228 // Filter object properties
229 filter: (obj, predicate) =>
230 Object.keys(obj).reduce((result, key) => {
231 if (predicate(obj[key], key)) {
232 result[key] = obj[key];
233 }
234 return result;
235 }, {}),
236
237 // Invert object keys/values
238 invert: (obj) =>
239 Object.keys(obj).reduce((result, key) => ({
240 ...result,
241 [obj[key]]: key
242 }), {})
243 };
244
245 // Statistical analysis with spread/rest
246 analyze(...datasets) {
247 const combined = [].concat(...datasets);
248 const sorted = [...combined].sort((a, b) => a - b);
249
250 return {
251 datasets: datasets.length,
252 total: combined.length,
253 sum: combined.reduce((sum, val) => sum + val, 0),
254 mean: combined.length > 0 ?
255 combined.reduce((sum, val) => sum + val, 0) / combined.length : 0,
256 median: sorted.length > 0 ?
257 sorted.length % 2 === 0
258 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2
259 : sorted[Math.floor(sorted.length / 2)]
260 : 0,
261 min: Math.min(...combined),
262 max: Math.max(...combined),
263 range: Math.max(...combined) - Math.min(...combined),
264 unique: [...new Set(combined)].length
265 };
266 }
267
268 // Export functionality
269 export(format = 'json', ...data) {
270 const timestamp = new Date().toISOString();
271 const exportData = {
272 timestamp,
273 stats: { ...this.stats },
274 data: data.length === 1 ? data[0] : data
275 };
276
277 switch (format.toLowerCase()) {
278 case 'csv':
279 return this.toCSV(exportData.data);
280 case 'json':
281 return JSON.stringify(exportData, null, 2);
282 case 'summary':
283 return `Data Export Summary:
284Generated: ${timestamp}
285Items processed: ${this.stats.processed}
286Cache hits: ${this.stats.cached}
287Errors: ${this.stats.errors}
288Data size: ${JSON.stringify(exportData).length} bytes`;
289 default:
290 return exportData;
291 }
292 }
293
294 toCSV(data) {
295 if (!Array.isArray(data) || data.length === 0) return '';
296
297 const headers = Object.keys(data[0]);
298 const csvRows = [
299 headers.join(','),
300 ...data.map(row =>
301 headers.map(header => {
302 const value = row[header];
303 return typeof value === 'string' ? `"${value.replace(/"/g, '""')}"` : value;
304 }).join(',')
305 )
306 ];
307
308 return csvRows.join('\n');
309 }
310
311 // Reset utilities
312 reset() {
313 this.cache.clear();
314 this.stats = { processed: 0, cached: 0, errors: 0 };
315 }
316}
317
318// Usage Examples and Demonstrations
319function demonstrateDataProcessor() {
320 console.log('🚀 Data Processor Demo Starting...');
321
322 // Initialize processor
323 const processor = new DataProcessor();
324
325 // Array processing demo
326 console.log('\n📊 Array Processing:');
327 const arrayResult = processor.processArrays(
328 [1, 2, 3, 4],
329 [3, 4, 5, 6],
330 [5, 6, 7, 8]
331 );
332 console.log('Combined arrays:', arrayResult.combined);
333 console.log('Unique values:', arrayResult.unique);
334 console.log('Statistics:', arrayResult.stats);
335
336 // Object merging demo
337 console.log('\n🔗 Object Merging:');
338 const base = { name: 'App', version: '1.0' };
339 const config1 = { features: { auth: true }, theme: 'dark' };
340 const config2 = { features: { analytics: true }, debug: true };
341
342 const merged = processor.mergeObjects(base, config1, config2);
343 console.log('Merged configuration:', merged);
344
345 // Transformation pipeline demo
346 console.log('\n🔄 Transformation Pipeline:');
347 const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
348
349 const pipeline = processor.pipe(
350 arr => arr.filter(n => n % 2 === 0), // Even numbers
351 arr => arr.map(n => n * n), // Square them
352 arr => arr.reduce((sum, n) => sum + n, 0) // Sum them
353 );
354
355 const result = pipeline(numbers);
356 console.log(`Pipeline result: ${result}`); // (2² + 4² + 6² + 8² + 10²) = 220
357
358 // Configuration building demo
359 console.log('\n⚙️ Configuration Building:');
360 const appConfig = processor.buildConfig(
361 { appName: 'MyApp', port: 3000 },
362 'development',
363 ['analytics', 'social'],
364 { customSetting: true, port: 8080 }
365 );
366 console.log('App configuration:', appConfig);
367
368 // Array utilities demo
369 console.log('\n🛠️ Array Utilities:');
370 const users = [
371 { id: 1, name: 'Alice', department: 'Engineering' },
372 { id: 2, name: 'Bob', department: 'Marketing' },
373 { id: 3, name: 'Charlie', department: 'Engineering' }
374 ];
375
376 const grouped = processor.arrayUtils.groupBy(users, 'department');
377 console.log('Grouped by department:', grouped);
378
379 const chunked = processor.arrayUtils.chunk(numbers, 3);
380 console.log('Chunked array:', chunked);
381
382 // Statistical analysis demo
383 console.log('\n📈 Statistical Analysis:');
384 const stats = processor.analyze(
385 [85, 92, 78, 96, 88], // Test scores set 1
386 [91, 87, 93, 89, 94], // Test scores set 2
387 [76, 82, 88, 91, 85] // Test scores set 3
388 );
389 console.log('Statistical analysis:', stats);
390
391 // Export demo
392 console.log('\n📤 Export Demo:');
393 const exportData = [
394 { name: 'Alice', score: 95, grade: 'A' },
395 { name: 'Bob', score: 87, grade: 'B' },
396 { name: 'Charlie', score: 92, grade: 'A' }
397 ];
398
399 console.log('JSON Export:');
400 console.log(processor.export('json', exportData));
401
402 console.log('\nCSV Export:');
403 console.log(processor.export('csv', exportData));
404
405 console.log('\nSummary Export:');
406 console.log(processor.export('summary', exportData));
407
408 console.log('\n✅ Demo completed successfully!');
409
410 return {
411 processor,
412 results: {
413 arrays: arrayResult,
414 merged,
415 pipelineResult: result,
416 config: appConfig,
417 grouped,
418 stats,
419 exportData
420 }
421 };
422}
423
424// Run the demonstration
425demonstrateDataProcessor();Outstanding work mastering spread and rest operators! You've learned powerful techniques for array manipulation, object updates, and flexible function design. In our next lesson, we'll explore Let, Const & Block Scope, where you'll understand variable scoping improvements, temporal dead zones, and how ES6 variable declarations solve common JavaScript pitfalls. Get ready to write more predictable, error-free code!
Your progress is saved in this browser, on this device only.