javascript

examples

examples.js
/**
 * ========================================
 * 11.2 Working with JSON - Examples
 * ========================================
 *
 * Comprehensive examples for JSON operations.
 */

/**
 * ========================================
 * EXAMPLE 1: Basic JSON.stringify
 * ========================================
 */
console.log('--- Example 1: Basic JSON.stringify ---');

const user = {
  name: 'John Doe',
  age: 30,
  email: 'john@example.com',
  isActive: true,
  scores: [95, 87, 92],
};

const jsonString = JSON.stringify(user);
console.log('JSON string:', jsonString);
console.log('Type:', typeof jsonString);

/**
 * ========================================
 * EXAMPLE 2: Pretty Printing
 * ========================================
 */
console.log('\n--- Example 2: Pretty Printing ---');

const data = {
  company: 'Tech Corp',
  employees: [
    { name: 'Alice', role: 'Developer' },
    { name: 'Bob', role: 'Designer' },
  ],
  locations: {
    headquarters: 'New York',
    branches: ['London', 'Tokyo'],
  },
};

console.log('Compact:');
console.log(JSON.stringify(data));

console.log('\nPretty (2 spaces):');
console.log(JSON.stringify(data, null, 2));

console.log('\nPretty (4 spaces):');
console.log(JSON.stringify(data, null, 4));

console.log('\nPretty (custom):');
console.log(JSON.stringify(data, null, '→ '));

/**
 * ========================================
 * EXAMPLE 3: Replacer Function
 * ========================================
 */
console.log('\n--- Example 3: Replacer Function ---');

const sensitiveData = {
  username: 'johndoe',
  password: 'secret123',
  email: 'john@example.com',
  ssn: '123-45-6789',
  balance: 5000,
};

// Remove sensitive fields
const safeJson = JSON.stringify(sensitiveData, (key, value) => {
  const sensitiveFields = ['password', 'ssn'];
  if (sensitiveFields.includes(key)) {
    return undefined; // Exclude from output
  }
  return value;
});

console.log('With sensitive data removed:', safeJson);

// Transform values
const transformedJson = JSON.stringify(sensitiveData, (key, value) => {
  if (key === 'password') return '******';
  if (key === 'ssn') return 'XXX-XX-' + value.slice(-4);
  if (key === 'balance') return '$' + value.toLocaleString();
  return value;
});

console.log('With transformed values:', transformedJson);

/**
 * ========================================
 * EXAMPLE 4: Replacer Array
 * ========================================
 */
console.log('\n--- Example 4: Replacer Array ---');

const fullUser = {
  id: 1,
  username: 'johndoe',
  email: 'john@example.com',
  password: 'secret',
  role: 'admin',
  createdAt: new Date().toISOString(),
  lastLogin: new Date().toISOString(),
};

// Only include specific fields
const publicFields = ['id', 'username', 'email', 'role'];
const publicJson = JSON.stringify(fullUser, publicFields, 2);

console.log('Public user data:');
console.log(publicJson);

/**
 * ========================================
 * EXAMPLE 5: toJSON Method
 * ========================================
 */
console.log('\n--- Example 5: toJSON Method ---');

class Product {
  constructor(id, name, price, cost) {
    this.id = id;
    this.name = name;
    this.price = price;
    this.cost = cost; // Internal, shouldn't be exposed
  }

  get profit() {
    return this.price - this.cost;
  }

  toJSON() {
    // Custom serialization
    return {
      id: this.id,
      name: this.name,
      price: this.price,
      formattedPrice: `$${this.price.toFixed(2)}`,
      // Note: cost is intentionally excluded
    };
  }
}

const product = new Product(1, 'Widget', 29.99, 15.0);
console.log('Product JSON:', JSON.stringify(product, null, 2));

/**
 * ========================================
 * EXAMPLE 6: Basic JSON.parse
 * ========================================
 */
console.log('\n--- Example 6: Basic JSON.parse ---');

const jsonData = '{"name":"Jane","age":25,"city":"Boston"}';

const parsed = JSON.parse(jsonData);
console.log('Parsed object:', parsed);
console.log('Name:', parsed.name);
console.log('Age:', parsed.age);
console.log('Type of age:', typeof parsed.age);

/**
 * ========================================
 * EXAMPLE 7: Reviver Function
 * ========================================
 */
console.log('\n--- Example 7: Reviver Function ---');

const eventJson = `{
    "title": "Conference",
    "startDate": "2024-12-15T09:00:00.000Z",
    "endDate": "2024-12-15T17:00:00.000Z",
    "attendees": 150
}`;

// Parse with date revival
const event = JSON.parse(eventJson, (key, value) => {
  // Check if value looks like an ISO date
  if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
    return new Date(value);
  }
  return value;
});

console.log('Event:', event);
console.log('Start date is Date:', event.startDate instanceof Date);
console.log('Event date:', event.startDate.toLocaleDateString());

/**
 * ========================================
 * EXAMPLE 8: Safe JSON Parsing
 * ========================================
 */
console.log('\n--- Example 8: Safe JSON Parsing ---');

function safeJSONParse(text, fallback = null) {
  if (typeof text !== 'string') {
    console.warn('Input is not a string');
    return fallback;
  }

  try {
    return JSON.parse(text);
  } catch (error) {
    console.warn('JSON parse error:', error.message);
    return fallback;
  }
}

console.log('Valid JSON:', safeJSONParse('{"valid": true}'));
console.log('Invalid JSON:', safeJSONParse('not json'));
console.log('Invalid with fallback:', safeJSONParse('broken', { error: true }));
console.log('Number input:', safeJSONParse(42));

/**
 * ========================================
 * EXAMPLE 9: Handling Dates
 * ========================================
 */
console.log('\n--- Example 9: Handling Dates ---');

const appointment = {
  title: 'Doctor Visit',
  date: new Date('2024-12-20T10:30:00'),
  reminder: new Date('2024-12-19T10:30:00'),
};

// Stringify (dates become ISO strings)
const appointmentJson = JSON.stringify(appointment);
console.log('Stringified:', appointmentJson);

// Parse without reviver (dates are strings)
const parsed1 = JSON.parse(appointmentJson);
console.log('Without reviver:');
console.log('  date type:', typeof parsed1.date);
console.log('  date value:', parsed1.date);

// Parse with date reviver
const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;

const parsed2 = JSON.parse(appointmentJson, (key, value) => {
  if (typeof value === 'string' && ISO_DATE_REGEX.test(value)) {
    return new Date(value);
  }
  return value;
});

console.log('With reviver:');
console.log('  date type:', typeof parsed2.date, parsed2.date instanceof Date);
console.log('  date value:', parsed2.date.toLocaleString());

/**
 * ========================================
 * EXAMPLE 10: Handling Maps and Sets
 * ========================================
 */
console.log('\n--- Example 10: Handling Maps and Sets ---');

// Problem: Maps and Sets don't serialize properly
const dataWithCollections = {
  name: 'Example',
  myMap: new Map([
    ['key1', 'value1'],
    ['key2', 'value2'],
  ]),
  mySet: new Set([1, 2, 3, 4, 5]),
};

console.log('Default stringify (loses data):');
console.log(JSON.stringify(dataWithCollections, null, 2));

// Solution: Custom serialization
function stringify(obj) {
  return JSON.stringify(
    obj,
    (key, value) => {
      if (value instanceof Map) {
        return {
          __type__: 'Map',
          __data__: Array.from(value.entries()),
        };
      }
      if (value instanceof Set) {
        return {
          __type__: 'Set',
          __data__: Array.from(value),
        };
      }
      return value;
    },
    2
  );
}

function parse(json) {
  return JSON.parse(json, (key, value) => {
    if (value && typeof value === 'object') {
      if (value.__type__ === 'Map') {
        return new Map(value.__data__);
      }
      if (value.__type__ === 'Set') {
        return new Set(value.__data__);
      }
    }
    return value;
  });
}

const serialized = stringify(dataWithCollections);
console.log('\nCustom stringify:');
console.log(serialized);

const restored = parse(serialized);
console.log('\nRestored:');
console.log('Map get key1:', restored.myMap.get('key1'));
console.log('Set has 3:', restored.mySet.has(3));

/**
 * ========================================
 * EXAMPLE 11: Circular References
 * ========================================
 */
console.log('\n--- Example 11: Circular References ---');

const person = {
  name: 'Alice',
  friends: [],
};

const friend = {
  name: 'Bob',
  friends: [person], // Reference to person
};

person.friends.push(friend); // Circular: person -> friend -> person

// This would throw: "Converting circular structure to JSON"
try {
  JSON.stringify(person);
} catch (e) {
  console.log('Error:', e.message);
}

// Solution: Track seen objects
function stringifyCircular(obj) {
  const seen = new WeakSet();

  return JSON.stringify(
    obj,
    (key, value) => {
      if (typeof value === 'object' && value !== null) {
        if (seen.has(value)) {
          return '[Circular Reference]';
        }
        seen.add(value);
      }
      return value;
    },
    2
  );
}

console.log('\nWith circular handling:');
console.log(stringifyCircular(person));

/**
 * ========================================
 * EXAMPLE 12: Deep Clone with JSON
 * ========================================
 */
console.log('\n--- Example 12: Deep Clone with JSON ---');

const original = {
  name: 'Original',
  nested: {
    value: 42,
    array: [1, 2, 3],
  },
};

// Deep clone
const clone = JSON.parse(JSON.stringify(original));

// Modify clone
clone.name = 'Clone';
clone.nested.value = 100;
clone.nested.array.push(4);

console.log('Original:', JSON.stringify(original, null, 2));
console.log('Clone:', JSON.stringify(clone, null, 2));
console.log('Original unchanged:', original.nested.value === 42);

/**
 * ========================================
 * EXAMPLE 13: Limitations of JSON Clone
 * ========================================
 */
console.log('\n--- Example 13: Limitations of JSON Clone ---');

const problematic = {
  date: new Date(),
  regex: /pattern/gi,
  func: function () {
    return 'hello';
  },
  undef: undefined,
  symbol: Symbol('id'),
  nan: NaN,
  infinity: Infinity,
  map: new Map([['a', 1]]),
  set: new Set([1, 2, 3]),
};

console.log('Original:');
console.log('  date:', problematic.date);
console.log('  regex:', problematic.regex);
console.log('  func:', typeof problematic.func);
console.log('  undef:', problematic.undef);
console.log('  nan:', problematic.nan);
console.log('  infinity:', problematic.infinity);

const cloned = JSON.parse(JSON.stringify(problematic));

console.log('\nAfter JSON clone:');
console.log('  date:', cloned.date, '(type:', typeof cloned.date + ')');
console.log('  regex:', cloned.regex, '(type:', typeof cloned.regex + ')');
console.log('  func:', cloned.func, '(lost!)');
console.log('  undef:', cloned.undef, '(lost!)');
console.log('  nan:', cloned.nan, '(became null)');
console.log('  infinity:', cloned.infinity, '(became null)');
console.log('  map:', cloned.map, '(became empty object)');
console.log('  set:', cloned.set, '(became empty object)');

/**
 * ========================================
 * EXAMPLE 14: structuredClone
 * ========================================
 */
console.log('\n--- Example 14: structuredClone ---');

const withSpecialTypes = {
  date: new Date(),
  regex: /pattern/gi,
  array: [1, 2, 3],
  nested: { deep: { value: 42 } },
  map: new Map([['key', 'value']]),
  set: new Set([1, 2, 3]),
};

// Modern deep clone
const structuredCopy = structuredClone(withSpecialTypes);

console.log('Date preserved:', structuredCopy.date instanceof Date);
console.log('Regex preserved:', structuredCopy.regex instanceof RegExp);
console.log('Map preserved:', structuredCopy.map instanceof Map);
console.log('Set preserved:', structuredCopy.set instanceof Set);

// Verify independence
structuredCopy.nested.deep.value = 100;
console.log(
  'Original value unchanged:',
  withSpecialTypes.nested.deep.value === 42
);

/**
 * ========================================
 * EXAMPLE 15: Custom Deep Clone
 * ========================================
 */
console.log('\n--- Example 15: Custom Deep Clone ---');

function deepClone(obj, seen = new WeakMap()) {
  // Handle primitives and null
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  // Handle circular references
  if (seen.has(obj)) {
    return seen.get(obj);
  }

  // Handle Date
  if (obj instanceof Date) {
    return new Date(obj.getTime());
  }

  // Handle RegExp
  if (obj instanceof RegExp) {
    return new RegExp(obj.source, obj.flags);
  }

  // Handle Map
  if (obj instanceof Map) {
    const clone = new Map();
    seen.set(obj, clone);
    obj.forEach((value, key) => {
      clone.set(deepClone(key, seen), deepClone(value, seen));
    });
    return clone;
  }

  // Handle Set
  if (obj instanceof Set) {
    const clone = new Set();
    seen.set(obj, clone);
    obj.forEach((value) => {
      clone.add(deepClone(value, seen));
    });
    return clone;
  }

  // Handle Array
  if (Array.isArray(obj)) {
    const clone = [];
    seen.set(obj, clone);
    for (let i = 0; i < obj.length; i++) {
      clone[i] = deepClone(obj[i], seen);
    }
    return clone;
  }

  // Handle Object
  const clone = Object.create(Object.getPrototypeOf(obj));
  seen.set(obj, clone);

  for (const key of Reflect.ownKeys(obj)) {
    const descriptor = Object.getOwnPropertyDescriptor(obj, key);
    if (descriptor.value !== undefined) {
      descriptor.value = deepClone(descriptor.value, seen);
    }
    Object.defineProperty(clone, key, descriptor);
  }

  return clone;
}

// Test with circular reference
const objWithCircular = { name: 'Test' };
objWithCircular.self = objWithCircular;
objWithCircular.date = new Date();
objWithCircular.map = new Map([['a', 1]]);

const clonedObj = deepClone(objWithCircular);
console.log('Cloned with circular:', clonedObj.self === clonedObj); // true
console.log('Date cloned:', clonedObj.date instanceof Date);
console.log('Map cloned:', clonedObj.map instanceof Map);

/**
 * ========================================
 * EXAMPLE 16: JSON Validation
 * ========================================
 */
console.log('\n--- Example 16: JSON Validation ---');

function validateJSON(data, schema) {
  const errors = [];

  for (const [key, rules] of Object.entries(schema)) {
    const value = data[key];

    // Required check
    if (rules.required && (value === undefined || value === null)) {
      errors.push(`${key} is required`);
      continue;
    }

    if (value === undefined || value === null) continue;

    // Type check
    if (rules.type) {
      const actualType = Array.isArray(value) ? 'array' : typeof value;
      if (actualType !== rules.type) {
        errors.push(`${key} must be type ${rules.type}, got ${actualType}`);
      }
    }

    // Number constraints
    if (typeof value === 'number') {
      if (rules.min !== undefined && value < rules.min) {
        errors.push(`${key} must be >= ${rules.min}`);
      }
      if (rules.max !== undefined && value > rules.max) {
        errors.push(`${key} must be <= ${rules.max}`);
      }
    }

    // String constraints
    if (typeof value === 'string') {
      if (rules.minLength && value.length < rules.minLength) {
        errors.push(`${key} must be at least ${rules.minLength} characters`);
      }
      if (rules.maxLength && value.length > rules.maxLength) {
        errors.push(`${key} must be at most ${rules.maxLength} characters`);
      }
      if (rules.pattern && !rules.pattern.test(value)) {
        errors.push(`${key} has invalid format`);
      }
    }

    // Array constraints
    if (Array.isArray(value)) {
      if (rules.minItems && value.length < rules.minItems) {
        errors.push(`${key} must have at least ${rules.minItems} items`);
      }
      if (rules.maxItems && value.length > rules.maxItems) {
        errors.push(`${key} must have at most ${rules.maxItems} items`);
      }
    }
  }

  return {
    valid: errors.length === 0,
    errors,
  };
}

const userSchema = {
  name: { required: true, type: 'string', minLength: 2, maxLength: 50 },
  email: {
    required: true,
    type: 'string',
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
  },
  age: { required: false, type: 'number', min: 0, max: 150 },
  tags: { required: false, type: 'array', minItems: 1, maxItems: 10 },
};

const validUser = { name: 'John Doe', email: 'john@example.com', age: 30 };
const invalidUser = { name: 'J', email: 'invalid', age: 200 };

console.log('Valid user:', validateJSON(validUser, userSchema));
console.log('Invalid user:', validateJSON(invalidUser, userSchema));

/**
 * ========================================
 * EXAMPLE 17: JSON Diff
 * ========================================
 */
console.log('\n--- Example 17: JSON Diff ---');

function jsonDiff(obj1, obj2, path = '') {
  const differences = [];

  // Get all keys from both objects
  const allKeys = new Set([
    ...Object.keys(obj1 || {}),
    ...Object.keys(obj2 || {}),
  ]);

  for (const key of allKeys) {
    const fullPath = path ? `${path}.${key}` : key;
    const val1 = obj1?.[key];
    const val2 = obj2?.[key];

    // Check if key was added
    if (val1 === undefined && val2 !== undefined) {
      differences.push({ type: 'added', path: fullPath, value: val2 });
      continue;
    }

    // Check if key was removed
    if (val1 !== undefined && val2 === undefined) {
      differences.push({ type: 'removed', path: fullPath, value: val1 });
      continue;
    }

    // Check if types are different
    const type1 = Array.isArray(val1) ? 'array' : typeof val1;
    const type2 = Array.isArray(val2) ? 'array' : typeof val2;

    if (type1 !== type2) {
      differences.push({
        type: 'changed',
        path: fullPath,
        from: val1,
        to: val2,
      });
      continue;
    }

    // Recursively check objects
    if (type1 === 'object' && val1 !== null) {
      differences.push(...jsonDiff(val1, val2, fullPath));
      continue;
    }

    // Check primitive values
    if (val1 !== val2) {
      differences.push({
        type: 'changed',
        path: fullPath,
        from: val1,
        to: val2,
      });
    }
  }

  return differences;
}

const before = {
  name: 'John',
  age: 30,
  address: { city: 'NYC', zip: '10001' },
  hobbies: ['reading'],
};

const after = {
  name: 'John',
  age: 31,
  address: { city: 'Boston', zip: '02101' },
  email: 'john@example.com',
};

console.log('Differences:');
jsonDiff(before, after).forEach((diff) => {
  if (diff.type === 'added') {
    console.log(`  + ${diff.path}: ${JSON.stringify(diff.value)}`);
  } else if (diff.type === 'removed') {
    console.log(`  - ${diff.path}: ${JSON.stringify(diff.value)}`);
  } else {
    console.log(
      `  ~ ${diff.path}: ${JSON.stringify(diff.from)} → ${JSON.stringify(
        diff.to
      )}`
    );
  }
});

/**
 * ========================================
 * Summary
 * ========================================
 */
console.log('\n========================================');
console.log('Working with JSON - Examples Complete');
console.log('========================================');
console.log('Key functions demonstrated:');
console.log('- JSON.stringify() with replacer and spacing');
console.log('- JSON.parse() with reviver');
console.log('- toJSON() for custom serialization');
console.log('- Handling dates, Maps, Sets');
console.log('- Circular reference handling');
console.log('- Deep cloning techniques');
console.log('- JSON validation and diffing');
Examples - JavaScript Tutorial | DeepML