Author: Amber Colvin

  • JavaScript data structures: choose by lookup, order, and uniqueness

    JavaScript data structures: choose by lookup, order, and uniqueness

    JavaScript data structures are easiest to choose when the required access pattern comes first. Use an array for indexed order, an object for simple string-keyed records, Map for flexible keyed lookup, and Set for uniqueness.

    These data structures in JavaScript also support stack and queue patterns. The best data structures JavaScript developers choose depend on whether code needs stable order, repeated values, fast membership checks, or removal from one end.

    When are arrays or objects the right choice?

    An array is an ordered, index-based collection. It allows duplicates and makes positional reads and updates straightforward, while searching for a value requires scanning. Use it for records displayed in sequence, batches, or data whose position matters.

    const colors = [“red”, “blue”, “red”]; The expression colors[1] returns “blue”, and colors.includes(“red”) checks for a value.

    A plain object is a record of properties identified by string or symbol keys. It suits fixed fields, configuration, and simple dictionaries. Numeric-looking keys are converted to strings, and inherited properties mean property checks should be deliberate.

    const user = { name: “Ari”, role: “editor” }; The expression user.role reads a known property directly.

    When should you use Map or Set instead of objects or arrays?

    Use Map when a collection is fundamentally a key-value lookup and keys may be any JavaScript value, including objects and functions. Map provides dedicated methods such as set, get, has, and delete, plus a size property.

    const prices = new Map([[“book”, 12]]); The expression prices.get(“book”) returns 12.

    Use Set when each value should occur only once. It provides membership checks and deletion by value, and it preserves insertion order during iteration, but it does not provide numeric indexes.

    const tags = new Set([“js”, “web”, “js”]); The set contains only “js” and “web”; tags.has(“web”) checks membership.

    Compare an object with Map by key rules, order, and operations. An object accepts string and symbol property keys and is record-oriented. Map accepts arbitrary key types, has collection-specific operations, and guarantees insertion-order iteration. Object enumeration follows property-key ordering rules, including special handling for integer-like keys, so the two structures are not interchangeable.

    Compare an array with Set by the same criteria. An array preserves duplicates, supports indexes, and can represent repeated events in sequence. Set enforces uniqueness and offers direct value membership, making it better for selected IDs, permissions, or visited nodes.

    How do stack and queue patterns work with arrays?

    A stack uses last-in, first-out access: the newest item is removed first. Arrays implement this pattern efficiently with push and pop.

    const stack = []; Then use stack.push(“draft”) to add an item and stack.pop() to remove the newest item.

    A queue uses first-in, first-out access: the oldest item is removed first. The simple array pattern adds with push and removes with shift.

    const queue = [“first”, “second”]; Use queue.push(“third”), then queue.shift() to remove “first”.

    Front removal with shift() can cost O(n) because remaining elements are reindexed. For a growing queue, keep a head index instead: const item = queue[head++]; This avoids repeatedly shifting every remaining element.

    How do JavaScript data structures compare by lookup, order, and uniqueness?

    • Keyed lookup: choose an object for simple string-keyed records, or Map for arbitrary key types and explicit map operations.
    • Order: choose an array for indexed positions, or Set when insertion order matters but duplicates must disappear.
    • Uniqueness: choose Set for automatic deduplication; arrays and objects require separate checks or transformation logic.
    • Removal pattern: use pop for a stack, shift for a small queue, or a head index for a queue with frequent front removals.
  • JavaScript Practice: Progressive Coding Challenges with Test Cases

    JavaScript Practice: Progressive Coding Challenges with Test Cases

    Build JavaScript practice through eight focused problems that increase in difficulty. Each exercise defines an input, required output, constraints, sample cases, and review criteria, so you can test behavior instead of relying on an open-ended project brief.

    Start with the first section, then move forward only when your implementation passes the listed edge cases. These JavaScript coding challenges are short enough for repeated attempts and code reviews.

    JavaScript Practice: Beginner String and Number Challenges

    Reverse a string

    Input: A string. Required output: The characters in reverse order. Constraints: Preserve every character, including spaces, and support empty input. Samples: "JavaScript" → "tpircSavaJ"; "" → "". Hint: Use a loop or reverse and join. Review: Check that no characters are lost and the source value is not mutated.

    Count vowels

    Input: A string. Required output: The number of a, e, i, o, and u characters. Constraints: Count case-insensitively and ignore nonletters. Samples: "JavaScript" → 3; "rhythm!" → 0. Hint: Normalize the string and use a vowel lookup. Review: Empty input must return 0, and uppercase vowels must count.

    Build FizzBuzz

    Input: A positive integer n. Required output: An array from 1 through n, replacing multiples of 3 with "Fizz", multiples of 5 with "Buzz", and multiples of both with "FizzBuzz". Constraints: 1 ≤ n ≤ 1,000. Samples: 5 → [1, 2, "Fizz", 4, "Buzz"]; 15 → [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]. Hint: Test divisibility by 15 first. Review: Preserve order and the number-versus-string value types.

    JavaScript Coding Challenges for Arrays and Objects

    Keep first occurrences

    Input: An integer array. Required output: A unique array preserving first-seen order. Constraints: Do not sort the values or change the source array. Samples: [3, 1, 3, 2, 1] → [3, 1, 2]; [] → []. Hint: Track seen values with a Set or lookup. Review: Test duplicates at the beginning, middle, and end, and verify the original array remains unchanged.

    Group records

    Input: An array of objects with name and team properties. Required output: An object mapping each team to an array of names. Constraints: Preserve input order and create no empty groups. Sample: [{name: "Ana", team: "red"}, {name: "Bo", team: "blue"}, {name: "Cy", team: "red"}] → {red: ["Ana", "Cy"], blue: ["Bo"]}. Hint: Build an accumulator as you iterate. Review: Check exact keys, name order, and behavior with an empty array.

    JavaScript Code Challenges for Functions and Data Transformation

    Compose unary functions

    Input: A list of synchronous unary functions and a value x. Required output: The result after applying functions from left to right. Constraints: Functions accept and return one value. Samples: [x=>x+2, x=>x*3], 4 → 18; [], 4 → 4. Hint: Reduce over the function list. Review: Confirm execution order and avoid mutating the list.

    Summarize a cart

    Input: An array of {price, qty} items and a tax rate. Required output: The total after tax, rounded to two decimals. Constraints: Prices, quantities, and tax are nonnegative; apply tax after calculating the subtotal. Samples: [{price: 10, qty: 2}], 0.1 → 22; [], 0.2 → 0. Hint: Reduce the subtotal before rounding. Review: Prevent string concatenation, handle zero items, and account for floating-point rounding.

    Async Extension: Reliable Fetch Behavior

    Retry an async request

    Input: A promise factory and a maximum-attempts integer. Required output: The first resolved value, or the final rejection after all attempts fail. Constraints: Call the factory once per attempt, require maxAttempts ≥ 1, and stop immediately after success. Samples: A factory yielding Error("E1"), Error("E2"), then "ok" with 3 attempts resolves to "ok" on call 3; two failures with maxAttempts=2 reject Error("E2"). Hint: Use an await loop with try/catch. Review: Preserve the final error and verify the exact attempt count.

  • JavaScript absolute value: How Math.abs() handles common inputs

    JavaScript absolute value: How Math.abs() handles common inputs

    JavaScript absolute value uses Math.abs() to return a number’s distance from zero. Negative values become positive, positive values stay unchanged, and zero remains zero. In other words, absolute value in JavaScript represents magnitude without a sign.

    The syntax is Math.abs(value). The function accepts one argument and applies JavaScript’s numeric conversion before calculating the result, so its behavior includes more than number literals.

    How does JavaScript absolute value work with Math.abs()?

    For ordinary numeric input, Math.abs() returns the input’s nonnegative magnitude. It does not change the original variable; use the returned value when the normalized number is needed.

    • Math.abs(-12) returns 12.
    • Math.abs(12) returns 12.
    • Math.abs(0) returns 0.
    • Math.abs(-3.75) returns 3.75.

    These results follow the mathematical definition: both -12 and 12 are 12 units from zero. The function removes the sign only when the value is negative; it does not round decimals or otherwise alter a finite number.

    What does absolute value in JavaScript return for strings and null?

    The JavaScript Math.abs function converts its argument to a number before applying absolute value. A string containing a valid numeric representation therefore produces a numeric result, while a string that cannot be converted produces NaN.

    • Math.abs(“-18”) returns 18.
    • Math.abs(“6.5”) returns 6.5.
    • Math.abs(” -9 “) returns 9; surrounding whitespace is ignored during conversion.
    • Math.abs(null) returns 0, because numeric conversion treats null as zero.
    • Math.abs(“18px”) returns NaN, because the complete string is not a valid number.
    • Math.abs(“hello”) returns NaN.

    An empty string also converts to zero, so Math.abs(“”) returns 0. By contrast, Math.abs(undefined) returns NaN. NaN signals that the conversion did not produce a usable numeric result; it is not a positive or negative number.

    How can Math.abs(a – b) find the distance between two numbers?

    Use Math.abs(a – b) to calculate the absolute difference between two numbers. Subtraction happens first, and Math.abs() then removes the direction of that difference. This makes the result the same regardless of which number is larger.

    For example, Math.abs(18 – 25) returns 7. Reversing the operands, as in Math.abs(25 – 18), also returns 7. The same pattern works with variables:

    const distance = Math.abs(currentValue – targetValue);

    If currentValue is 42 and targetValue is 50, distance is 8. This pattern is useful for comparing measurements, positions, scores, or other numeric values where only the size of the difference matters.

    How does JavaScript Math.abs handle NaN, Infinity, and -0?

    Special numeric values follow predictable rules:

    • Math.abs(NaN) returns NaN. An unknown numeric result remains unknown.
    • Math.abs(Infinity) returns Infinity.
    • Math.abs(-Infinity) returns Infinity.
    • Math.abs(-0) returns positive zero, displayed as 0.

    JavaScript distinguishes positive and negative zero internally even though both usually display as 0. After Math.abs(-0), the sign is positive. The function also returns NaN when called without an argument: Math.abs() has no value to convert.

  • Check If String Is Empty in JavaScript

    Check If String Is Empty in JavaScript

    Use strict equality to check if string is empty when the input is already known to be a string: value === ”. To check if a string is empty in a validation rule, choose a different test when spaces should count as no content or when the value may be missing.

    The practical answer to how to check if a string is empty depends on the definition of “empty”: an exact zero-character value, whitespace-only text, or an absent value.

    Check if string is empty: value === ” or value.length === 0

    For a value guaranteed to be a primitive string, these two checks identify an exact empty string:

    • value === ” compares the value directly with JavaScript’s empty string literal. It does not coerce numbers, booleans, or other values.
    • value.length === 0 checks whether the string contains zero characters. It produces the same result for a known string.

    Strict equality is usually the clearest option because it states the intended value directly. The length form is useful when you are already working with string properties or want to emphasize the character count.

    Neither check treats a string containing spaces as empty. For example, ‘ ‘ === ” is false, and the string’s length is greater than zero.

    Treat whitespace-only text as empty with value.trim().length === 0

    Use value.trim().length === 0 when leading and trailing whitespace should not count as content. The trim() method removes whitespace from both ends of a string, so this condition matches all of these examples:

    • ”
    • ‘ ‘
    • ‘ ’
    • ‘ text ‘ does not match because text remains after trimming

    trim() returns a new string and does not modify the original value. Use the exact equality or length check instead when spaces are meaningful, such as in a fixed-format value where padding must be preserved.

    For checking an empty string in JavaScript when the input type is not guaranteed, combine trimming with a type guard:

    typeof value === ‘string’ && value.trim().length === 0

    This prevents trim() from being called on a number, object, or other non-string value.

    Handle null or undefined before checking

    null and undefined are missing-value states, not empty strings. They require a separate decision. Accessing value.length or calling value.trim() on either value throws a TypeError, so guard them before using string methods.

    An explicit null or undefined guard is:

    value === null || value === undefined

    For an optional field where missing values should be rejected separately from empty text, use conditions such as:

    • value === null || value === undefined for a missing value
    • typeof value === ‘string’ && value.length === 0 for an exact empty string
    • typeof value === ‘string’ && value.trim().length === 0 for empty or whitespace-only text

    If your rule defines both missing and blank as invalid, combine those cases explicitly:

    value === null || value === undefined || (typeof value === ‘string’ && value.trim().length === 0)

    This condition does not silently classify unrelated types as empty. The shorter expression value == null also matches only null and undefined, but the explicit form makes that intent easier to read.

    Avoid truthiness surprises with !value and optional values

    !value tests whether a value is falsy, not whether it is an empty string. It evaluates to true for an empty string, but also for:

    • 0
    • false
    • null
    • undefined
    • NaN

    It also does not identify whitespace-only text: !’ ‘ is false because a non-empty string is truthy. Use !value only when every falsy value should be treated as invalid. If zero or false are valid inputs, use a strict string, trimming, or null check that matches the required definition of empty.

  • JavaScript object methods explained: this, ownership, and prototypes

    JavaScript object methods explained: this, ownership, and prototypes

    JavaScript object methods are functions stored as object properties. The call site determines the value of this, while the method’s location determines whether it is an own property or inherited through a prototype.

    The same ideas apply to object literals, instances created by classes, and built-in APIs such as Object.keys. A small set of concrete examples makes the differences clear.

    JavaScript object methods: function properties, shorthand syntax, and calls

    A function becomes an object method when it is assigned to a property and invoked through that object. The traditional function-property syntax looks like this:

    const user = { name: “Mina”, greet: function () { return “Hi, ” + this.name; } };

    Calling user.greet() executes the function with user as its receiver. The method shorthand provides the same behavior with less syntax:

    const user = { name: “Mina”, greet() { return “Hi, ” + this.name; } };

    Here, user.greet() returns “Hi, Mina”. Shorthand is not a special kind of function. It is concise syntax for defining a regular method in an object literal. The important detail is the property access followed by parentheses: user.greet().

    How this gets its value from the call-site receiver

    For regular object methods, this usually comes from the object before the dot. In user.greet(), the receiver is user. Calling the same function through another object changes the receiver:

    const colleague = { name: “Ravi” }; user.greet.call(colleague);

    This call returns “Hi, Ravi”. The function has not changed; only its receiver has.

    Detaching the function removes that receiver:

    const speak = user.greet; speak();

    In strict-mode code, including JavaScript modules and class methods, this is undefined in the detached call. The method therefore cannot read this.name. Use user.greet() when the receiver should remain attached, or create a bound function with const speak = user.greet.bind(user).

    An arrow function does not acquire its own receiver from a method call. Its this is lexical, inherited from the surrounding scope. Therefore, greet: () => this.name is not a replacement for a regular receiver-based method when the function needs the object that appears before the dot.

    Object methods: own properties versus instance and class prototype methods

    Methods defined directly in an object literal are own properties. The function belongs to that particular object:

    const literalUser = { name: “Mina”, greet() { return “Hi, ” + this.name; } };

    A class uses a different ownership model:

    class User { constructor(name) { this.name = name; } greet() { return “Hi, ” + this.name; } } const mina = new User(“Mina”);

    mina.name is an own property because the constructor creates it on the instance. mina.greet works, but the function is stored on User.prototype, not copied onto mina. JavaScript looks along the prototype chain when it does not find a property directly on the instance.

    Object.hasOwn(mina, “name”) returns true, while Object.hasOwn(mina, “greet”) returns false. Prototype sharing lets every User instance use one method function. Assigning a function inside the constructor would instead create a separate own method for every instance.

    JS object methods and useful static Object methods

    Some object methods are static methods on the built-in Object constructor. They are called as Object.method(), not as methods of the data object. For example:

    Object.keys(user) returns an array of the object’s enumerable own property names, such as [“name”, “greet”]. Related static methods include Object.values(user) for values and Object.entries(user) for key-value pairs.

    Object.hasOwn(user, “name”) checks own-property ownership without searching the prototype chain. Object.assign(target, source) copies enumerable properties into a target object, while Object.create(prototype) creates an object with a chosen prototype.

    These static APIs differ from instance or prototype methods such as user.greet() and user.toString(). The first group is called on Object; the second is reached through a specific object and may use that object as the receiver.

  • JavaScript Calendar: Build a Month Grid with Events

    JavaScript Calendar: Build a Month Grid with Events

    Build a JavaScript calendar that renders a navigable month grid and marks events from structured data with native JavaScript. Keep month state separate from event data so each navigation click can rerender the grid without losing the current view.

    Keep the calendar JavaScript in one module: the same render function can serve the initial load and every previous or next month action.

    Set up the JavaScript calendar HTML and event data

    Give the interface three hooks: a month label, previous and next buttons, and an empty grid. For example, use data-calendar-label, data-calendar-previous, data-calendar-next, and data-calendar-grid attributes. Query them with document.querySelector so the widget does not depend on a particular framework.

    Store events with normalized local date keys. The date uses a four-digit year, two-digit month, and two-digit day:

    const events = [{ date: ‘2025-01-08’, title: ‘Planning review’ }, { date: ‘2025-01-21’, title: ‘Release’ }];

    Build an index once, outside the renderer, so multiple events can share a date:

    const eventsByDate = events.reduce((map, event) => { const list = map.get(event.date) || []; list.push(event); map.set(event.date, list); return map; }, new Map());

    Use a state object such as const state = { year: 2025, month: 0 }; JavaScript months are zero-based: 0 means January and 11 means December. The displayed month number is therefore the internal month plus one.

    Calculate first weekdays, month lengths, and grid cells

    Calculate the first weekday and number of days whenever the month is rendered:

    const firstWeekday = new Date(state.year, state.month, 1).getDay();
    const daysInMonth = new Date(state.year, state.month + 1, 0).getDate();

    getDay() returns Sunday as 0 and Saturday as 6. The second expression uses day 0 of the following month, which returns the final day of the selected month. This avoids hard-coded month lengths and handles leap years.

    Reserve enough cells for the leading blank cells before day 1 and the trailing cells after the final day:

    const cellCount = Math.ceil((firstWeekday + daysInMonth) / 7) * 7;

    For cell index i, calculate the visible day with const day = i – firstWeekday + 1;. Values below 1 are leading cells; values above daysInMonth are trailing cells. Render those positions as empty, noninteractive cells rather than inventing dates.

    Render a JavaScript calendar widget with event markers

    Clear the grid at the start of every render, then loop from 0 to cellCount – 1. For each valid day, create a button or cell and generate a stable key:

    const toKey = (year, month, day) => `${year}-${String(month + 1).padStart(2, ‘0’)}-${String(day).padStart(2, ‘0’)}`;

    For each valid day, create a button, set button.dataset.date = key, and set its visible text to the day number. Retrieve matching events with const dayEvents = eventsByDate.get(key) || []; Then add an event marker when the array is not empty, such as a bullet or count. Use the event titles for the button’s accessible label or title, for example, dayEvents.map(event => event.title).join(‘, ‘).

    This stable date key lets click handlers identify a day without parsing its displayed number. It also keeps events attached correctly when the month changes. A JavaScript calendar widget can use the same structure for birthdays, appointments, or release dates.

    Add previous and next controls to a JavaScript event calendar

    Attach click handlers that update the zero-based month and then call render():

    previous.addEventListener(‘click’, () => { if (state.month === 0) { state.month = 11; state.year–; } else state.month–; render(); });

    next.addEventListener(‘click’, () => { if (state.month === 11) { state.month = 0; state.year++; } else state.month++; render(); });

    Update the label inside render() with new Intl.DateTimeFormat(‘en’, { month: ‘long’, year: ‘numeric’ }).format(new Date(state.year, state.month, 1)). Keep the state object and event index outside the renderer. Reinitializing either one inside render() would reset navigation or discard event data. Call render() once after wiring the controls to display the initial month.

  • JavaScript replaceAll: How to Replace Every Match

    JavaScript replaceAll: How to Replace Every Match

    JavaScript replaceAll replaces every occurrence of matching text and returns a new string. It does not modify the original string, so you must store or use the returned value.

    Use replaceAll in JavaScript with a literal string when the text is known, or with a global regular expression when matching needs flags or pattern logic.

    How JavaScript replaceAll Replaces Every Literal Match

    Pass a string as the first argument to search for literal text. Regular-expression characters in that argument, such as + or ., have no special meaning.

    Example: const source = “red, red, blue”;

    const updated = source.replaceAll(“red”, “green”);

    console.log(updated); // “green, green, blue”

    console.log(source); // “red, red, blue”

    The returned value contains every replacement, while source remains unchanged. Literal matching is case-sensitive, so searching for “red” does not replace “Red”. A replacement can be a fixed string or a function that calculates the result for each match.

    How Does JS replaceAll Differ from replace?

    JS replaceAll replaces every literal match by default. The replace() method replaces only the first literal match when its search value is a string.

    Example: const source = “red, red, blue”;

    source.replace(“red”, “green”); // “green, red, blue”

    source.replaceAll(“red”, “green”); // “green, green, blue”

    With a regular expression, replace() replaces every match only when the expression has the global g flag:

    source.replace(/red/g, “green”); // “green, green, blue”

    Use replaceAll() for clear literal replacement. Use replace() when you intentionally need only the first match, or when existing code already handles a regular-expression pattern.

    How to Use replaceAll() in JavaScript with a Global Regex

    A regular expression passed to replaceAll() must be global. The expression must include the g flag, or JavaScript throws a TypeError instead of performing a partial replacement.

    Example: const text = “Red red RED”;

    const result = text.replaceAll(/red/gi, “blue”);

    // “blue blue blue”

    The i flag makes the pattern case-insensitive, while g makes it apply to every match. A non-global expression such as /red/i is not valid for replaceAll().

    For dynamic text that should be treated literally, pass the value directly as a string:

    const term = “red+blue”;

    text.replaceAll(term, “color”);

    If a dynamic value must be included in a larger regular expression, escape its pattern characters before constructing the expression:

    function escapeRegExp(value) {

    return value.replace(/[.*+?^${}()|[\]\\]/g, “\\$&”);

    }

    const pattern = new RegExp(escapeRegExp(term), “g”);

    text.replaceAll(pattern, “color”);

    This keeps characters from the user-provided value from changing the regex’s meaning and preserves the required global behavior.

    How Do Callbacks and Special Replacement Tokens Work?

    Pass a function as the replacement to calculate a different result for each match. The callback receives the complete match first, followed by captured groups and other match details.

    Example: const prices = “Item: $5, Item: $12”;

    const raised = prices.replaceAll(/\$(\d+)/g, (match, amount) => “$” + (Number(amount) + 1));

    // “Item: $6, Item: $13”

    When the replacement is a string, JavaScript recognizes special replacement tokens. $& inserts the complete match, $1 inserts the first captured group, $2 inserts the second, $` inserts the text before the match, $’ inserts the text after the match, and $$ inserts a literal dollar sign.

    Example: const marked = “red blue”.replaceAll(/(\w+)/g, “[$&]”);

    // “[red] [blue]”

    These tokens are interpreted when the replacement is a string, including when the search value is a literal string. A callback’s returned value is inserted as-is, so dollar signs returned by the function are not treated as replacement tokens.

  • Get Object Keys in JavaScript: Find, Read, and Iterate

    Get Object Keys in JavaScript: Find, Read, and Iterate

    To get object keys in JavaScript, use JavaScript Object.keys() to list an object’s own enumerable string keys. Use bracket notation to read a property whose name is stored in a variable, and use Object.entries() to process keys and values together.

    Finding a key and accessing a property are separate operations. Object.keys() discovers available names; bracket access retrieves a value after you choose a name. The methods below focus on own properties, so inherited data does not silently enter your results.

    Get object keys in JavaScript with Object.keys()

    Object.keys(source) returns a new array containing the object’s own enumerable property names that are strings. It excludes inherited properties, non-enumerable properties, and symbols. The result is shallow: it lists keys on the source object, not keys inside nested objects.

    • const profile = { name: “Mina”, role: “editor” };
    • Object.keys(profile) returns [“name”, “role”].

    The returned array is useful for displaying field names, checking whether an object has any enumerable data, or building a controlled iteration. An object with no own enumerable string properties produces an empty array.

    Get a key from an object in JavaScript, then read its value

    When you already know a key, do not search the key array to read its value. Dot notation works for a known identifier-like property name, such as profile.name. Bracket notation evaluates an expression, making it the required form for dynamic names, such as profile[field]. It also handles names that are not valid identifiers, including “display-name”.

    const field = “role”; const value = profile[field]; stores “editor” in value. In contrast, profile.field looks for a property literally named field; it does not use the variable’s contents. Reading a missing property returns undefined, whether the missing name is supplied with dot or bracket notation.

    Use JavaScript Object.keys(), Object.values(), and Object.entries()

    JavaScript Object.keys, Object.values(), and Object.entries() use the same selection rule: they include an object’s own enumerable string-keyed properties. They differ in what each returns:

    • Object.keys(profile) returns an array of property names.
    • Object.values(profile) returns the corresponding values, without their names.
    • Object.entries(profile) returns pairs such as [“name”, “Mina”].

    Use Object.values() when only the data matters. Use Object.entries() when each operation needs both the property name and its value:

    for (const [key, value] of Object.entries(profile)) { console.log(key, value); }

    This pattern avoids a second lookup and works naturally with destructuring. It iterates only own enumerable string keys, matching the behavior of Object.keys().

    Check own, symbol, and inherited keys with hasOwn and Reflect.ownKeys()

    Object.hasOwn(object, key) checks whether a property belongs directly to the object. It returns true for an own property even when that property is non-enumerable, and false for an inherited property. It is safer and clearer than calling a possibly overridden object.hasOwnProperty() method.

    • const parent = { inherited: true };
    • const record = Object.create(parent); record.visible = true;
    • Object.hasOwn(record, “visible”) returns true.
    • Object.hasOwn(record, “inherited”) returns false.

    Reflect.ownKeys(record) returns every own property key, including enumerable and non-enumerable string keys plus symbol keys. This is broader than Object.keys(), Object.values(), and Object.entries(), which omit symbols and non-enumerable properties. Use Object.getOwnPropertyNames() for all own string keys, or Object.getOwnPropertySymbols() for own symbol keys.

    A for…in loop is different: it visits enumerable string keys from the object and its prototype chain. If you use one, guard each key with Object.hasOwn(record, key). For own enumerable data, Object.keys() or Object.entries() is usually clearer.

  • How to Check for an Empty Object in JavaScript

    How to Check for an Empty Object in JavaScript

    To check for an empty object in JavaScript, first define empty as “no own enumerable string-keyed properties.” For a value already known to be a plain object, Object.keys(obj).length === 0 is the direct test. To safely check an empty object in JavaScript when input may be null or another type, add guards before calling Object.keys().

    A JavaScript empty object can still have inherited, symbol, or non-enumerable properties, so the result depends on which properties your application considers.

    How to check for an empty object in JavaScript with Object.keys(obj).length === 0

    Object.keys() returns an array containing an object’s own enumerable string keys. An object literal with no such keys produces an empty array:

    const value = {};
    Object.keys(value).length === 0; // true

    An object with an own enumerable property produces a nonzero length:

    const value = { name: “Ada” };
    Object.keys(value).length === 0; // false

    When the input is expected to be an object but may be null, use a type and null guard:

    const isEmpty = value =>
      value !== null &&
      typeof value === “object” &&
      Object.keys(value).length === 0;

    This condition also returns true for arrays, dates, maps, sets, and some class instances that have no enumerable own string properties. Use the stricter plain-object check below when the value’s type is not already controlled.

    Guard an empty JavaScript object check against null, arrays, Map, Set, Date, and class instances

    typeof null is “object”, but Object.keys(null) throws a TypeError. Check for null before the type test. Arrays also have object types, so a separate array check or a plain-object prototype check is needed.

    This function accepts object literals and objects created with a null prototype, while rejecting arrays and built-in or custom instance types:

    const isEmptyPlainObject = value =>
      value !== null &&
      typeof value === “object” &&
      (Object.getPrototypeOf(value) === Object.prototype ||
       Object.getPrototypeOf(value) === null) &&
      Object.keys(value).length === 0;

    An array should usually be tested with value.length === 0. A Map and a Set store data in internal collection slots, so use map.size === 0 or set.size === 0. A Date is a date value rather than an empty record. A class instance may have no own fields while still representing a meaningful domain object; test its required fields or use an instanceof check before applying an emptiness rule.

    What does a JavaScript empty object mean for own, inherited, symbol, and non-enumerable properties?

    Object.keys() counts only own, enumerable, string-named properties. Each category changes the result:

    • Own properties: properties stored directly on the object. Enumerable own properties appear in Object.keys().
    • Inherited properties: properties found on the object’s prototype. They do not appear in Object.keys(), and this is normally desirable for configuration or data objects.
    • Symbol properties: own symbol-keyed properties are not returned by Object.keys(), even when they are enumerable.
    • Non-enumerable properties: own string or symbol properties marked non-enumerable are also excluded.

    For example, an object can have hidden data while appearing empty to Object.keys():

    const value = {};
    Object.defineProperty(value, “id”, { value: 7 });
    Object.keys(value).length === 0; // true

    To count all own string and symbol keys, including non-enumerable keys, use Reflect.ownKeys(value).length === 0. Object.getOwnPropertyNames() includes all own string keys, while Object.getOwnPropertySymbols() returns own symbol keys. A for…in loop is not equivalent to Object.keys() because it can include inherited enumerable properties.

    Choose a definition of emptiness when checking whether an object is empty in JavaScript

    • For a plain data object, define empty as having no own enumerable string keys and use the guarded Object.keys(value).length === 0 test.
    • For an object where symbols or non-enumerable metadata matter, use Reflect.ownKeys(value).length === 0 after validating the value’s type.
    • For collections, use their native size or length: Map.size, Set.size, or array length.
    • For inherited settings, decide explicitly whether the prototype contributes data. Avoid treating inherited properties as own record fields by accident.

    Do not use JSON.stringify(value) === “{}” as a universal emptiness test. Serialization omits symbols, non-enumerable properties, and several values that do not serialize as ordinary object keys. It also says nothing about whether the value is a plain object, so property inspection and type validation are more reliable.

  • JavaScript Not-Equal Operators: !== vs !=

    JavaScript Not-Equal Operators: !== vs !=

    Use !== for most not-equal operators in JavaScript. It performs a strict comparison, so values must have the same type and value to be considered equal. Use != only when you deliberately want JavaScript to convert values before comparing them.

    These operators can produce different results for the same pair. Choosing the right one prevents unexpected matches between numbers, strings, booleans, and nullish values.

    JavaScript Not-Equal Operators: Choose !== or !=

    !== is the strict inequality operator. It is the negation of strict equality: a !== b produces the same result as !(a === b). It returns true when the operands have different types or different values.

    != is the loose inequality operator. It is the negation of loose equality: a != b produces the same result as !(a == b). Before deciding whether values differ, JavaScript may convert one operand to another type.

    For predictable JavaScript not-equal comparisons, prefer !==. Reserve != for cases where that conversion is an intentional part of the condition.

    How does JavaScript strict inequality with !== work?

    Strict inequality checks type and value without coercion. Two numbers with different values are not equal, and a number is not strictly equal to a string containing the same digits.

    • 7 !== 4 is true because the number values differ.
    • 7 !== “7” is true because one operand is a number and the other is a string.
    • “ready” !== “ready” is false because both operands are the same string.
    • null !== undefined is true because they are different types.

    Strict inequality also follows JavaScript’s strict equality rules for special numeric values. In particular, NaN !== NaN is true, while 0 !== -0 is false.

    How does JavaScript not-equal comparison with != handle type coercion?

    Loose inequality applies the abstract equality rules before returning the opposite result. A numeric string can be converted to a number, and boolean values can be converted to numbers. This means operands that look different in source code may compare as equal.

    • 7 != “7” is false. The string is converted to the number 7.
    • 0 != false is false. The boolean is converted to 0.
    • “” != 0 is false. The empty string is converted to 0.
    • null != undefined is false. These two values are treated as equal by loose equality.

    Coercion can be useful when an input is expected to contain a numeric or boolean representation. It can also hide invalid data, so a loose condition should document or clearly reflect that intent.

    Which results change for numbers, strings, null, and NaN?

    Which value pairs make != and !== disagree?

    The operators disagree whenever loose equality converts the operands into matching values or applies its special nullish rule.

    • 5 and “5”: 5 != “5” is false, but 5 !== “5” is true.
    • 0 and false: 0 != false is false, but 0 !== false is true.
    • null and undefined: null != undefined is false, but null !== undefined is true.

    For ordinary unequal values such as 3 and 8, both operators return true. The difference appears when types or special values affect the comparison.

    Why does NaN need an explicit check?

    NaN means “Not-a-Number,” but it is still a number value according to typeof. It is not equal to itself, so both NaN != NaN and NaN !== NaN return true. Testing whether two values differ does not identify NaN specifically.

    Use Number.isNaN(value) for an explicit check. Unlike the global isNaN() function, Number.isNaN() does not first coerce unrelated values such as strings.

    How do you write clear JavaScript not-equal checks?

    • Use value !== expected when both type and value must match.
    • Use typeof value !== “string” when rejecting values of a particular type.
    • Use value !== null when only the literal null should be excluded.
    • Use value != null only when intentionally treating both null and undefined as missing.
    • Convert input explicitly when conversion is required, then compare strictly: Number(input) !== 10.

    In most not-equal checks in JS, !== communicates the intended rule directly and avoids implicit conversion. Choose != only when its coercive behavior is known, tested, and desirable.