Facebook Pixel

2727. Is Object Empty

Problem Description

The problem asks you to determine whether a given object or array is empty.

An empty object is defined as an object that contains no key-value pairs. For example, {} is empty, while {a: 1} is not empty.

An empty array is defined as an array that contains no elements. For example, [] is empty, while [1] is not empty.

The input will be either a JavaScript object or an array that is guaranteed to be valid output from JSON.parse. This means you don't need to handle invalid JSON or other data types.

Your task is to implement a function isEmpty that:

  • Takes one parameter: either an object or an array
  • Returns true if the input is empty (no properties for objects, no elements for arrays)
  • Returns false if the input contains at least one property or element

The solution uses a for...in loop to iterate through the properties of the object or elements of the array. If the loop enters even once, it means there's at least one property/element, so the function immediately returns false. If the loop never executes (because there are no properties/elements), the function returns true.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that both objects and arrays in JavaScript can be iterated using the for...in loop. This loop will iterate over all enumerable properties of an object or all indices of an array.

If an object or array is empty, the for...in loop will never execute its body because there are no properties or indices to iterate over. On the other hand, if there's even a single property or element, the loop will execute at least once.

This leads to a clever approach: instead of checking the length or counting properties, we can simply try to enter the loop. If we can enter the loop even once, we know the object/array is not empty, so we immediately return false. If we never enter the loop, we know it's empty and return true after the loop.

This approach works uniformly for both objects and arrays without needing to distinguish between them. For objects, for...in iterates over keys like "name", "age", etc. For arrays, it iterates over indices like "0", "1", "2", etc. In both cases, an empty structure means the loop body never executes.

The elegance of this solution lies in its simplicity - we don't need to use Object.keys(), check length properties, or write separate logic for objects versus arrays. The for...in loop handles both cases with the same logic, making the code concise and efficient.

Solution Approach

The implementation uses a single for...in loop to check if the object or array is empty:

function isEmpty(obj: Record<string, any> | any[]): boolean {
    for (const x in obj) {
        return false;
    }
    return true;
}

Let's walk through how this works step by step:

  1. The for...in loop: This loop iterates over all enumerable properties of an object or all indices of an array. The variable x will hold the property name or array index during each iteration.

  2. Early return pattern: Inside the loop body, we immediately return false. This means if the loop executes even once (indicating at least one property or element exists), the function returns false right away.

  3. Default return: If the loop never executes (because there are no properties or elements to iterate over), the code continues past the loop and returns true.

Example walkthrough with an empty object {}:

  • The for...in loop attempts to iterate over the object
  • Since there are no properties, the loop body never executes
  • The function continues to the return true statement
  • Result: true (the object is empty)

Example walkthrough with a non-empty array [1, 2]:

  • The for...in loop starts iterating
  • On the first iteration, x would be "0" (the first index)
  • The loop body executes and immediately returns false
  • Result: false (the array is not empty)

This solution has O(1) time complexity in the best case (non-empty input) since it returns immediately upon finding the first property/element. In the worst case (empty input), it's still O(1) as there's nothing to iterate. The space complexity is O(1) as we only use a single loop variable.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with two concrete examples to see how the for...in loop approach works:

Example 1: Non-empty object {name: "Alice", age: 25}

function isEmpty(obj) {
    for (const x in obj) {
        return false;  // <-- We'll hit this line
    }
    return true;
}

isEmpty({name: "Alice", age: 25})

Step-by-step execution:

  1. The for...in loop starts and finds the first property "name"
  2. The loop variable x is set to "name"
  3. The loop body executes immediately
  4. We return false right away - no need to check "age" or any other properties
  5. Result: false (object is not empty)

Example 2: Empty array []

function isEmpty(obj) {
    for (const x in obj) {
        return false;  // <-- We never reach this line
    }
    return true;      // <-- We end up here
}

isEmpty([])

Step-by-step execution:

  1. The for...in loop attempts to iterate over array indices
  2. Since the array has no elements, there are no indices (no "0", "1", etc.)
  3. The loop condition fails immediately - the loop body never executes
  4. Execution continues past the loop to the next statement
  5. We return true
  6. Result: true (array is empty)

The beauty of this approach is that we don't need to check if the input is an object or array, count properties, or use different methods - the for...in loop handles both cases identically. As soon as we find any property or element, we know it's not empty and return immediately.

Solution Implementation

1def isEmpty(obj):
2    """
3    Checks if an object (dict) or list is empty (has no elements/keys)
4  
5    Args:
6        obj: The dict or list to check for emptiness
7      
8    Returns:
9        bool: True if the dict/list is empty, False otherwise
10    """
11    # Iterate through all elements/keys in the object
12    for key in obj:
13        # If any element/key exists, the object is not empty
14        return False
15    # No elements/keys found, the object is empty
16    return True
17
1/**
2 * Checks if an object (Map) or array is empty (has no elements)
3 * @param obj - The Map or array to check for emptiness
4 * @return true if the Map/array is empty, false otherwise
5 */
6public static boolean isEmpty(Object obj) {
7    // Check if the input is a Map
8    if (obj instanceof Map) {
9        Map<?, ?> map = (Map<?, ?>) obj;
10        // If the map has any entries, it's not empty
11        return map.isEmpty();
12    }
13    // Check if the input is an array
14    else if (obj != null && obj.getClass().isArray()) {
15        // Get the length of the array using reflection
16        int length = java.lang.reflect.Array.getLength(obj);
17        // Return true if array length is 0, false otherwise
18        return length == 0;
19    }
20    // Check if the input is a Collection (List, Set, etc.)
21    else if (obj instanceof Collection) {
22        Collection<?> collection = (Collection<?>) obj;
23        // Return true if collection has no elements
24        return collection.isEmpty();
25    }
26    // For null or unsupported types, consider as empty
27    return true;
28}
29
1#include <unordered_map>
2#include <vector>
3#include <any>
4
5/**
6 * Checks if a container (map or vector) is empty
7 * @param container - The container to check for emptiness
8 * @returns true if the container is empty, false otherwise
9 */
10template<typename T>
11bool isEmpty(const T& container) {
12    // Check if the container has no elements
13    return container.empty();
14}
15
16// Overload for unordered_map (similar to JavaScript object)
17bool isEmpty(const std::unordered_map<std::string, std::any>& obj) {
18    // Check if the map has no key-value pairs
19    return obj.empty();
20}
21
22// Overload for vector (similar to JavaScript array)
23template<typename T>
24bool isEmpty(const std::vector<T>& arr) {
25    // Check if the vector has no elements
26    return arr.empty();
27}
28
29// Alternative implementation that mimics the original JavaScript logic more closely
30template<typename T>
31bool isEmptyIterative(const T& container) {
32    // Iterate through the container
33    for (auto it = container.begin(); it != container.end(); ++it) {
34        // If any element exists, the container is not empty
35        return false;
36    }
37    // No elements found, the container is empty
38    return true;
39}
40
1/**
2 * Checks if an object or array is empty (has no enumerable properties)
3 * @param obj - The object or array to check for emptiness
4 * @returns true if the object/array is empty, false otherwise
5 */
6function isEmpty(obj: Record<string, any> | any[]): boolean {
7    // Iterate through all enumerable properties of the object/array
8    for (const key in obj) {
9        // If any property exists, the object/array is not empty
10        return false;
11    }
12    // No properties found, the object/array is empty
13    return true;
14}
15

Time and Space Complexity

Time Complexity: O(1)

The function uses a for...in loop to iterate over the properties of the object or array. However, the loop returns immediately upon encountering the first property/element. In the worst case (empty object/array), the loop doesn't iterate at all and returns true. In the best case (non-empty object/array), it returns false on the first iteration. Therefore, the function performs at most one iteration, making the time complexity constant O(1).

Space Complexity: O(1)

The function only uses a single loop variable x to store the current property key during iteration. No additional data structures are created that scale with the input size. The space usage remains constant regardless of the size of the input object or array, resulting in O(1) space complexity.

Common Pitfalls

Pitfall 1: Confusing for...in behavior with inherited properties

In JavaScript, for...in loops iterate over all enumerable properties in the prototype chain, not just the object's own properties. While this isn't an issue for arrays or objects created from JSON.parse() (which only have own properties), it can cause unexpected behavior in other contexts.

Example of the pitfall:

const obj = Object.create({ inherited: true });
// obj has no own properties, but has an inherited property

console.log(isEmpty(obj)); // Returns false (unexpected!)
// The for...in loop sees the inherited property

Solution: Use hasOwnProperty() check or Object.keys():

function isEmpty(obj) {
    for (const x in obj) {
        if (obj.hasOwnProperty(x)) {
            return false;
        }
    }
    return true;
}

// Alternative approach using Object.keys()
function isEmpty(obj) {
    return Object.keys(obj).length === 0;
}

Pitfall 2: Performance considerations for sparse arrays

When dealing with sparse arrays (arrays with gaps in indices), for...in only iterates over defined indices, which might be counterintuitive compared to checking the length property.

Example of the pitfall:

const sparseArray = new Array(1000);
sparseArray[999] = 'value';
// This array has length 1000 but only one actual element

// Using for...in (current solution) - iterates only once
isEmpty(sparseArray); // Returns false quickly

// If someone mistakenly uses array.length
function isEmptyWrong(arr) {
    return arr.length === 0; // Would return false for empty sparse arrays
}

Solution: The current for...in approach actually handles this correctly, but developers should be aware that it checks for actual elements, not the array's length property.

Pitfall 3: Python-specific consideration with different iterable types

In Python, the iteration behavior differs slightly between dictionaries and lists. When iterating over a dictionary, you iterate over keys, while for lists you iterate over values.

Example of the pitfall:

# This works correctly but might be confusing
d = {"key": "value"}
for item in d:  # item is "key", not "value"
    print(item)  # Prints: "key"

l = ["value"]
for item in l:  # item is "value"
    print(item)  # Prints: "value"

Solution: The current implementation works correctly because we only care about the existence of items, not their values. However, for clarity, you might want to be explicit:

def isEmpty(obj):
    # More explicit approach
    if isinstance(obj, dict):
        return len(obj.keys()) == 0
    elif isinstance(obj, list):
        return len(obj) == 0
  
    # Or simply use len() for both
    return len(obj) == 0
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which algorithm should you use to find a node that is close to the root of the tree?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More