Master advanced JavaScript concepts including RegExp, Dates, JSON, and Web APIs.
Master advanced JavaScript concepts including RegExp, Dates, JSON, and Web APIs.
Master regular expressions, pattern matching, and text processing techniques for advanced string manipulation and validation
Content by: Yash Patel
React.js Developer
Regular expressions are powerful tools for pattern matching and text manipulation. They provide a concise and flexible way to search, replace, and validate text data in JavaScript applications.
// Creating regular expressions
// Literal syntax
const pattern1 = /hello/;
const pattern2 = /hello/i; // Case insensitive
// Constructor syntax
const pattern3 = new RegExp('hello');
const pattern4 = new RegExp('hello', 'i');
// Basic patterns
const wordPattern = /\b\w+\b/; // Word boundaries
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const phonePattern = /^\+?[1-9]\d{1,14}$/;
// Character classes
const digitPattern = /\d/; // Any digit
const wordCharPattern = /\w/; // Word character
const whitespacePattern = /\s/; // Whitespace
const nonDigitPattern = /\D/; // Non-digit
const nonWordPattern = /\W/; // Non-word character
const nonWhitespacePattern = /\S/; // Non-whitespace
// Quantifiers
const zeroOrMore = /a*/; // Zero or more 'a'
const oneOrMore = /a+/; // One or more 'a'
const zeroOrOne = /a?/; // Zero or one 'a'
const exactCount = /a{3}/; // Exactly 3 'a'
const rangeCount = /a{2,4}/; // 2 to 4 'a'
const atLeastCount = /a{2,}/; // At least 2 'a'
// Anchors
const startAnchor = /^hello/; // Starts with 'hello'
const endAnchor = /world$/; // Ends with 'world'
const wordBoundary = /\bword\b/; // Word boundary
// Groups and capturing
const groupPattern = /(\d{3})-(\d{3})-(\d{4})/; // Phone number groups
const nonCapturingGroup = /(?:\d{3})-(\d{3})-(\d{4})/; // Non-capturing group
// Alternation
const orPattern = /cat|dog|bird/; // cat or dog or bird
// Flags
const globalFlag = /hello/g; // Global search
const caseInsensitiveFlag = /hello/i; // Case insensitive
const multilineFlag = /^hello/m; // Multiline
const stickyFlag = /hello/y; // Sticky
const unicodeFlag = /hello/u; // Unicode
// Test method
console.log(/hello/.test('hello world')); // true
console.log(/hello/.test('world')); // false
// Exec method
const match = /(\d{3})-(\d{3})-(\d{4})/.exec('123-456-7890');
console.log(match); // ['123-456-7890', '123', '456', '7890']
// String methods with RegExp
const text = 'Hello world, hello universe';
console.log(text.match(/hello/gi)); // ['Hello', 'hello']
console.log(text.replace(/hello/gi, 'hi')); // 'Hi world, hi universe'
console.log(text.search(/world/)); // 6
console.log(text.split(/\s+/)); // ['Hello', 'world,', 'hello', 'universe']
// Advanced RegExp patterns
// Email validation
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
// Password validation (8+ chars, 1 uppercase, 1 lowercase, 1 digit)
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]{8,}$/;
// URL validation
const urlRegex = /^https?:\/\/([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?$/;
// Credit card validation
const creditCardRegex = /^(\d{4}[\s-]?){4}$/;
// Date validation (YYYY-MM-DD)
const dateRegex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
// Phone number validation
const phoneRegex = /^\+?1?[-.\s]?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})$/;
// Social Security Number validation
const ssnRegex = /^\d{3}-\d{2}-\d{4}$/;
// IP address validation
const ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
// HTML tag matching
const htmlTagRegex = /<[^>]*>/g;
// CSS selector validation
const cssSelectorRegex = /^[.#]?[a-zA-Z][a-zA-Z0-9_-]*$/;
// Function to validate patterns
function validatePattern(text, pattern) {
return pattern.test(text);
}
// Test validation functions
console.log(validatePattern('user@example.com', emailRegex)); // true
console.log(validatePattern('weak', passwordRegex)); // false
console.log(validatePattern('https://example.com', urlRegex)); // true
console.log(validatePattern('1234-5678-9012-3456', creditCardRegex)); // true
// RegExp with Unicode
const unicodeRegex = /\p{L}/u; // Any letter
const emojiRegex = /\p{Emoji}/u; // Any emoji
const currencyRegex = /\p{Sc}/u; // Any currency symbol
// Lookahead and lookbehind
const positiveLookahead = /\d(?=\s)/; // Digit followed by space
const negativeLookahead = /\d(?!\s)/; // Digit not followed by space
const positiveLookbehind = /(?<=\$)\d/; // Digit preceded by dollar sign
const negativeLookbehind = /(?<!\$)\d/; // Digit not preceded by dollar sign
// Named capture groups
const namedGroupRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const dateMatch = namedGroupRegex.exec('2023-12-25');
console.log(dateMatch.groups); // { year: '2023', month: '12', day: '25' }
// RegExp with template literals
function createPattern(domain) {
return new RegExp(`^[a-zA-Z0-9._%+-]+@${domain.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`);
}
const gmailPattern = createPattern('gmail.com');
console.log(gmailPattern.test('user@gmail.com')); // true
// Exercise: Build a Text Processor with RegExp
class TextProcessor {
constructor() {
this.patterns = new Map();
this.initializePatterns();
}
initializePatterns() {
this.patterns.set('email', /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g);
this.patterns.set('phone', /\+?1?[-.\s]?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})/g);
this.patterns.set('url', /https?:\/\/([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?/g);
this.patterns.set('date', /\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])/g);
this.patterns.set('creditCard', /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g);
}
extract(text, type) {
const pattern = this.patterns.get(type);
if (!pattern) {
throw new Error(`Unknown pattern type: ${type}`);
}
return text.match(pattern) || [];
}
mask(text, type, maskChar = '*') {
const pattern = this.patterns.get(type);
if (!pattern) {
throw new Error(`Unknown pattern type: ${type}`);
}
return text.replace(pattern, (match) => {
if (type === 'email') {
const [local, domain] = match.split('@');
return `${local.charAt(0)}${maskChar.repeat(local.length - 2)}${local.charAt(local.length - 1)}@${domain}`;
} else if (type === 'phone') {
return match.replace(/\d/g, maskChar);
} else if (type === 'creditCard') {
return match.replace(/\d(?=\d{4})/g, maskChar);
}
return maskChar.repeat(match.length);
});
}
validate(text, type) {
const pattern = this.patterns.get(type);
if (!pattern) {
throw new Error(`Unknown pattern type: ${type}`);
}
return pattern.test(text);
}
replace(text, type, replacement) {
const pattern = this.patterns.get(type);
if (!pattern) {
throw new Error(`Unknown pattern type: ${type}`);
}
return text.replace(pattern, replacement);
}
addPattern(name, pattern) {
this.patterns.set(name, pattern);
}
}
// Exercise: Build a RegExp Builder
class RegExpBuilder {
constructor() {
this.parts = [];
this.flags = '';
}
start() {
this.parts.push('^');
return this;
}
end() {
this.parts.push('$');
return this;
}
digit() {
this.parts.push('\d');
return this;
}
letter() {
this.parts.push('[a-zA-Z]');
return this;
}
word() {
this.parts.push('\w');
return this;
}
whitespace() {
this.parts.push('\s');
return this;
}
literal(text) {
this.parts.push(text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
return this;
}
oneOrMore() {
this.parts.push('+');
return this;
}
zeroOrMore() {
this.parts.push('*');
return this;
}
zeroOrOne() {
this.parts.push('?');
return this;
}
exactly(count) {
this.parts.push(`{${count}}`);
return this;
}
between(min, max) {
this.parts.push(`{${min},${max}}`);
return this;
}
or(...alternatives) {
this.parts.push(`(${alternatives.join('|')})`);
return this;
}
group(content) {
this.parts.push(`(${content})`);
return this;
}
nonCapturingGroup(content) {
this.parts.push(`(?:\${content})`);
return this;
}
caseInsensitive() {
this.flags += 'i';
return this;
}
global() {
this.flags += 'g';
return this;
}
multiline() {
this.flags += 'm';
return this;
}
build() {
const pattern = this.parts.join('');
return new RegExp(pattern, this.flags);
}
}
// Test the exercises
const processor = new TextProcessor();
const sampleText = `
Contact us at john@example.com or call +1-555-123-4567.
Visit https://example.com for more information.
Credit card: 1234-5678-9012-3456
Date: 2023-12-25
`;
console.log('Extracted emails:', processor.extract(sampleText, 'email'));
console.log('Extracted phones:', processor.extract(sampleText, 'phone'));
console.log('Masked text:', processor.mask(sampleText, 'email'));
const builder = new RegExpBuilder();
const emailPattern = builder
.start()
.word()
.zeroOrMore()
.literal('@')
.word()
.oneOrMore()
.literal('.')
.letter()
.between(2, 4)
.end()
.caseInsensitive()
.build();
console.log('Built pattern:', emailPattern);
console.log('Email validation:', emailPattern.test('user@example.com'));
Test your understanding of this topic:
Learn to work with dates, times, timezones, and modern date manipulation using the Date object and modern date libraries
Content by: Ujjwal Dangi
MERN Stack Developer
JavaScript provides built-in Date objects for working with dates and times. Understanding how to create, manipulate, and format dates is essential for building applications that deal with temporal data.
// Creating dates
const now = new Date();
const specificDate = new Date('2023-12-25');
const dateFromComponents = new Date(2023, 11, 25); // Month is 0-indexed
const dateFromTimestamp = new Date(1703462400000);
// Date methods
console.log(now.getFullYear()); // Current year
console.log(now.getMonth()); // Current month (0-11)
console.log(now.getDate()); // Current day
console.log(now.getDay()); // Current day of week (0-6)
console.log(now.getHours()); // Current hour
console.log(now.getMinutes()); // Current minute
console.log(now.getSeconds()); // Current second
console.log(now.getMilliseconds()); // Current millisecond
// UTC methods
console.log(now.getUTCFullYear());
console.log(now.getUTCHours());
console.log(now.getUTCDay());
// Setting date components
const date = new Date();
date.setFullYear(2024);
date.setMonth(0); // January
date.setDate(1);
date.setHours(12, 30, 0, 0); // Hour, minute, second, millisecond
// Date arithmetic
const futureDate = new Date();
futureDate.setDate(futureDate.getDate() + 7); // Add 7 days
futureDate.setMonth(futureDate.getMonth() + 1); // Add 1 month
futureDate.setFullYear(futureDate.getFullYear() + 1); // Add 1 year
// Date comparison
const date1 = new Date('2023-01-01');
const date2 = new Date('2023-12-31');
console.log(date1 < date2); // true
console.log(date1 > date2); // false
console.log(date1.getTime() === date2.getTime()); // false
// Date formatting
const date3 = new Date();
console.log(date3.toString()); // Full date string
console.log(date3.toDateString()); // Date only
console.log(date3.toTimeString()); // Time only
console.log(date3.toISOString()); // ISO format
console.log(date3.toLocaleDateString()); // Localized date
console.log(date3.toLocaleTimeString()); // Localized time
// Custom date formatting
function formatDate(date, format = 'YYYY-MM-DD') {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return format
.replace('YYYY', year)
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
}
console.log(formatDate(now, 'YYYY-MM-DD HH:mm:ss'));
// Date parsing
function parseDate(dateString) {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
throw new Error('Invalid date string');
}
return date;
}
// Timezone handling
function getTimezoneOffset() {
const date = new Date();
const offset = date.getTimezoneOffset();
const hours = Math.abs(Math.floor(offset / 60));
const minutes = Math.abs(offset % 60);
const sign = offset > 0 ? '-' : '+';
return `UTC${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
}
console.log('Timezone offset:', getTimezoneOffset());
// Date utilities
function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
function addMonths(date, months) {
const result = new Date(date);
result.setMonth(result.getMonth() + months);
return result;
}
function addYears(date, years) {
const result = new Date(date);
result.setFullYear(result.getFullYear() + years);
return result;
}
function isLeapYear(year) {
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
}
function getDaysInMonth(year, month) {
return new Date(year, month + 1, 0).getDate();
}
function isWeekend(date) {
const day = date.getDay();
return day === 0 || day === 6;
}
function isBusinessDay(date) {
return !isWeekend(date);
}
// Date range utilities
function getDateRange(startDate, endDate) {
const dates = [];
const current = new Date(startDate);
while (current <= endDate) {
dates.push(new Date(current));
current.setDate(current.getDate() + 1);
}
return dates;
}
function getBusinessDays(startDate, endDate) {
return getDateRange(startDate, endDate).filter(isBusinessDay);
}
// Age calculation
function calculateAge(birthDate) {
const today = new Date();
const birth = new Date(birthDate);
let age = today.getFullYear() - birth.getFullYear();
const monthDiff = today.getMonth() - birth.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
age--;
}
return age;
}
console.log('Age:', calculateAge('1990-05-15'));
// Exercise: Build a Date Utility Library
class DateUtils {
constructor() {
this.locale = 'en-US';
this.timezone = 'UTC';
}
setLocale(locale) {
this.locale = locale;
return this;
}
setTimezone(timezone) {
this.timezone = timezone;
return this;
}
format(date, format = 'YYYY-MM-DD') {
const d = new Date(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
const hours = String(d.getHours()).padStart(2, '0');
const minutes = String(d.getMinutes()).padStart(2, '0');
const seconds = String(d.getSeconds()).padStart(2, '0');
return format
.replace('YYYY', year)
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
}
parse(dateString) {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
throw new Error(`Invalid date: ${dateString}`);
}
return date;
}
add(date, amount, unit = 'days') {
const d = new Date(date);
switch (unit) {
case 'days':
d.setDate(d.getDate() + amount);
break;
case 'months':
d.setMonth(d.getMonth() + amount);
break;
case 'years':
d.setFullYear(d.getFullYear() + amount);
break;
case 'hours':
d.setHours(d.getHours() + amount);
break;
case 'minutes':
d.setMinutes(d.getMinutes() + amount);
break;
case 'seconds':
d.setSeconds(d.getSeconds() + amount);
break;
}
return d;
}
subtract(date, amount, unit = 'days') {
return this.add(date, -amount, unit);
}
diff(date1, date2, unit = 'days') {
const d1 = new Date(date1);
const d2 = new Date(date2);
const diffMs = d2.getTime() - d1.getTime();
switch (unit) {
case 'days':
return Math.floor(diffMs / (1000 * 60 * 60 * 24));
case 'hours':
return Math.floor(diffMs / (1000 * 60 * 60));
case 'minutes':
return Math.floor(diffMs / (1000 * 60));
case 'seconds':
return Math.floor(diffMs / 1000);
case 'milliseconds':
return diffMs;
}
}
isBetween(date, start, end) {
const d = new Date(date);
const s = new Date(start);
const e = new Date(end);
return d >= s && d <= e;
}
isWeekend(date) {
const d = new Date(date);
const day = d.getDay();
return day === 0 || day === 6;
}
isBusinessDay(date) {
return !this.isWeekend(date);
}
getBusinessDays(start, end) {
const dates = [];
const current = new Date(start);
const endDate = new Date(end);
while (current <= endDate) {
if (this.isBusinessDay(current)) {
dates.push(new Date(current));
}
current.setDate(current.getDate() + 1);
}
return dates;
}
getWeekNumber(date) {
const d = new Date(date);
d.setHours(0, 0, 0, 0);
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
const yearStart = new Date(d.getFullYear(), 0, 1);
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
}
getQuarter(date) {
const month = new Date(date).getMonth();
return Math.floor(month / 3) + 1;
}
getFiscalYear(date, startMonth = 0) {
const d = new Date(date);
const year = d.getFullYear();
const month = d.getMonth();
if (month < startMonth) {
return year - 1;
}
return year;
}
}
// Exercise: Build a Calendar Generator
class CalendarGenerator {
constructor(year, month) {
this.year = year;
this.month = month;
this.dateUtils = new DateUtils();
}
generate() {
const firstDay = new Date(this.year, this.month, 1);
const lastDay = new Date(this.year, this.month + 1, 0);
const startDate = new Date(firstDay);
startDate.setDate(startDate.getDate() - firstDay.getDay());
const calendar = [];
const current = new Date(startDate);
for (let week = 0; week < 6; week++) {
const weekDays = [];
for (let day = 0; day < 7; day++) {
weekDays.push({
date: new Date(current),
isCurrentMonth: current.getMonth() === this.month,
isToday: this.dateUtils.format(current) === this.dateUtils.format(new Date()),
isWeekend: this.dateUtils.isWeekend(current)
});
current.setDate(current.getDate() + 1);
}
calendar.push(weekDays);
}
return calendar;
}
getMonthName() {
return new Date(this.year, this.month).toLocaleDateString(this.dateUtils.locale, { month: 'long' });
}
getYearName() {
return this.year.toString();
}
nextMonth() {
if (this.month === 11) {
this.year++;
this.month = 0;
} else {
this.month++;
}
}
previousMonth() {
if (this.month === 0) {
this.year--;
this.month = 11;
} else {
this.month--;
}
}
}
// Test the exercises
const dateUtils = new DateUtils();
const today = new Date();
const future = dateUtils.add(today, 7, 'days');
const past = dateUtils.subtract(today, 30, 'days');
console.log('Today:', dateUtils.format(today));
console.log('Future:', dateUtils.format(future));
console.log('Past:', dateUtils.format(past));
console.log('Days between:', dateUtils.diff(past, future, 'days'));
console.log('Is weekend:', dateUtils.isWeekend(today));
const calendar = new CalendarGenerator(2024, 0); // January 2024
const monthCalendar = calendar.generate();
console.log(`Calendar for ${calendar.getMonthName()} ${calendar.getYearName()}:`);
monthCalendar.forEach(week => {
week.forEach(day => {
const marker = day.isToday ? '*' : day.isCurrentMonth ? ' ' : '.';
process.stdout.write(`${day.date.getDate().toString().padStart(2)}${marker} `);
});
console.log();
});
Test your understanding of this topic:
Master JSON parsing, stringification, and data serialization techniques for working with APIs and data storage
Content by: Bansi Patel
Node.js Developer
JSON (JavaScript Object Notation) is a lightweight data interchange format that's easy for humans to read and write, and easy for machines to parse and generate. It's the standard format for API communication and data storage.
// JSON.stringify - Convert JavaScript to JSON
const user = {
name: 'John Doe',
age: 30,
email: 'john@example.com',
isActive: true,
hobbies: ['reading', 'coding', 'gaming'],
address: {
street: '123 Main St',
city: 'New York',
zipCode: '10001'
},
birthDate: new Date('1993-05-15'),
undefinedValue: undefined,
functionValue: function() { return 'hello'; }
};
// Basic stringification
const jsonString = JSON.stringify(user);
console.log(jsonString);
// Stringify with indentation
const prettyJson = JSON.stringify(user, null, 2);
console.log(prettyJson);
// Stringify with replacer function
const replacer = (key, value) => {
if (key === 'email') {
return value.replace(/./g, '*'); // Mask email
}
if (typeof value === 'function') {
return undefined; // Remove functions
}
if (value instanceof Date) {
return value.toISOString(); // Convert dates
}
return value;
};
const maskedJson = JSON.stringify(user, replacer, 2);
console.log(maskedJson);
// Stringify with replacer array
const selectedFields = JSON.stringify(user, ['name', 'age', 'hobbies'], 2);
console.log(selectedFields);
// JSON.parse - Convert JSON to JavaScript
const parsedUser = JSON.parse(jsonString);
console.log(parsedUser.name); // 'John Doe'
// Parse with reviver function
const reviver = (key, value) => {
if (key === 'birthDate') {
return new Date(value);
}
if (key === 'email' && typeof value === 'string') {
return value.toLowerCase();
}
return value;
};
const revivedUser = JSON.parse(jsonString, reviver);
console.log(revivedUser.birthDate instanceof Date); // true
// Error handling
function safeParse(jsonString) {
try {
return JSON.parse(jsonString);
} catch (error) {
console.error('JSON parse error:', error.message);
return null;
}
}
function safeStringify(obj) {
try {
return JSON.stringify(obj);
} catch (error) {
console.error('JSON stringify error:', error.message);
return null;
}
}
// Deep cloning with JSON
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
// Data validation
function validateJSON(data, schema) {
const requiredFields = schema.required || [];
const fieldTypes = schema.types || {};
for (const field of requiredFields) {
if (!(field in data)) {
throw new Error(`Missing required field: ${field}`);
}
}
for (const [field, expectedType] of Object.entries(fieldTypes)) {
if (field in data) {
const actualType = typeof data[field];
if (actualType !== expectedType) {
throw new Error(`Field ${field} should be ${expectedType}, got ${actualType}`);
}
}
}
return true;
}
// Schema definition
const userSchema = {
required: ['name', 'email'],
types: {
name: 'string',
age: 'number',
email: 'string',
isActive: 'boolean'
}
};
// Test validation
try {
validateJSON(user, userSchema);
console.log('User data is valid');
} catch (error) {
console.error('Validation error:', error.message);
}
// JSON utilities
class JSONUtils {
static parseFile(fileContent) {
return safeParse(fileContent);
}
static stringifyFile(data, pretty = true) {
return pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
}
static merge(...objects) {
return objects.reduce((result, obj) => ({ ...result, ...obj }), {});
}
static flatten(obj, prefix = '') {
const flattened = {};
for (const [key, value] of Object.entries(obj)) {
const newKey = prefix ? `${prefix}.${key}` : key;
if (value && typeof value === 'object' && !Array.isArray(value)) {
Object.assign(flattened, this.flatten(value, newKey));
} else {
flattened[newKey] = value;
}
}
return flattened;
}
static unflatten(obj) {
const unflattened = {};
for (const [key, value] of Object.entries(obj)) {
const keys = key.split('.');
let current = unflattened;
for (let i = 0; i < keys.length - 1; i++) {
const k = keys[i];
current[k] = current[k] || {};
current = current[k];
}
current[keys[keys.length - 1]] = value;
}
return unflattened;
}
static diff(obj1, obj2) {
const allKeys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
const diff = {};
for (const key of allKeys) {
if (JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])) {
diff[key] = {
old: obj1[key],
new: obj2[key]
};
}
}
return diff;
}
static patch(obj, patch) {
const result = { ...obj };
for (const [key, value] of Object.entries(patch)) {
if (value === null) {
delete result[key];
} else {
result[key] = value;
}
}
return result;
}
}
// Test JSON utilities
const obj1 = { a: 1, b: { c: 2, d: 3 } };
const obj2 = { a: 1, b: { c: 2, d: 4 }, e: 5 };
console.log('Flattened:', JSONUtils.flatten(obj1));
console.log('Diff:', JSONUtils.diff(obj1, obj2));
console.log('Patched:', JSONUtils.patch(obj1, { b: { c: 2, d: 4 }, e: 5 }));
// Exercise: Build a JSON Data Manager
class JSONDataManager {
constructor() {
this.data = new Map();
this.schemas = new Map();
}
setSchema(name, schema) {
this.schemas.set(name, schema);
}
validate(data, schemaName) {
const schema = this.schemas.get(schemaName);
if (!schema) {
throw new Error(`Schema '${schemaName}' not found`);
}
return validateJSON(data, schema);
}
set(key, value, schemaName = null) {
if (schemaName) {
this.validate(value, schemaName);
}
this.data.set(key, JSON.parse(JSON.stringify(value)));
return this;
}
get(key) {
const value = this.data.get(key);
return value ? JSON.parse(JSON.stringify(value)) : null;
}
delete(key) {
return this.data.delete(key);
}
has(key) {
return this.data.has(key);
}
keys() {
return Array.from(this.data.keys());
}
values() {
return Array.from(this.data.values());
}
size() {
return this.data.size;
}
clear() {
this.data.clear();
}
export() {
const exportData = {};
for (const [key, value] of this.data) {
exportData[key] = value;
}
return JSON.stringify(exportData, null, 2);
}
import(jsonString) {
const data = JSON.parse(jsonString);
for (const [key, value] of Object.entries(data)) {
this.data.set(key, value);
}
}
query(filter) {
const results = [];
for (const [key, value] of this.data) {
if (this.matchesFilter(value, filter)) {
results.push({ key, value });
}
}
return results;
}
matchesFilter(value, filter) {
for (const [key, expectedValue] of Object.entries(filter)) {
if (value[key] !== expectedValue) {
return false;
}
}
return true;
}
}
// Exercise: Build a JSON API Client
class JSONApiClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.headers = {
'Content-Type': 'application/json'
};
}
setHeader(key, value) {
this.headers[key] = value;
return this;
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const config = {
headers: { ...this.headers, ...options.headers },
...options
};
if (options.body) {
config.body = JSON.stringify(options.body);
}
const response = await fetch(url, config);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
}
async get(endpoint) {
return this.request(endpoint, { method: 'GET' });
}
async post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: data
});
}
async put(endpoint, data) {
return this.request(endpoint, {
method: 'PUT',
body: data
});
}
async delete(endpoint) {
return this.request(endpoint, { method: 'DELETE' });
}
async patch(endpoint, data) {
return this.request(endpoint, {
method: 'PATCH',
body: data
});
}
}
// Test the exercises
const dataManager = new JSONDataManager();
// Set up schemas
dataManager.setSchema('user', {
required: ['name', 'email'],
types: { name: 'string', email: 'string', age: 'number' }
});
dataManager.setSchema('product', {
required: ['name', 'price'],
types: { name: 'string', price: 'number', category: 'string' }
});
// Add data
dataManager.set('user:1', {
name: 'John Doe',
email: 'john@example.com',
age: 30
}, 'user');
dataManager.set('product:1', {
name: 'Laptop',
price: 999.99,
category: 'Electronics'
}, 'product');
// Query data
const users = dataManager.query({ age: 30 });
console.log('Users aged 30:', users);
// Export/Import
const exported = dataManager.export();
console.log('Exported data:', exported);
const newManager = new JSONDataManager();
newManager.import(exported);
console.log('Imported data size:', newManager.size());
// Test API client
const api = new JSONApiClient('https://api.example.com')
.setHeader('Authorization', 'Bearer token123');
// Simulate API calls (these would fail without a real server)
// api.get('/users')
// .then(users => console.log('Users:', users))
// .catch(error => console.error('API error:', error));
Test your understanding of this topic:
Explore modern Web APIs, browser capabilities, and advanced features for building cutting-edge web applications
Content by: Raj Koradiya
Node.js Developer
Modern browsers provide a rich set of Web APIs that enable powerful features like geolocation, file handling, media capture, and more. Understanding these APIs is crucial for building feature-rich web applications.
Test your understanding of this topic:
Master advanced JavaScript patterns, design principles, and best practices for building maintainable, scalable applications
Content by: Vaibhav Nakrani
.Net Developer
Advanced JavaScript patterns help developers write more maintainable, efficient, and scalable code. These patterns include design principles, architectural approaches, and optimization techniques.
Test your understanding of this topic:
Continue your learning journey and master the next set of concepts.
Continue to Module 9