2690. Infinite Method Object 🔒
Problem Description
This problem asks you to create a function that returns a special object with "infinite methods". This object has a unique property: you can call any method name on it (even methods that don't exist), and it will always return the name of the method you called as a string.
The key characteristics of this infinite-method object are:
- It accepts any method call, regardless of whether that method was previously defined
- When any method is called, it returns the method's name as a string
- The method names can be anything - they don't need to be predefined
For example:
- If you call
obj.abc123()
, it returns the string"abc123"
- If you call
obj.hello()
, it returns the string"hello"
- If you call
obj.anyRandomName()
, it returns the string"anyRandomName"
The solution uses JavaScript's Proxy
object to intercept property access. When any property is accessed on the proxy object, it returns a function. When that function is called, it returns the property name as a string. The get
trap in the proxy handler intercepts all property accesses and returns a function that, when executed, returns the property name converted to a string.
This creates the illusion of an object with infinite methods, where each "method" simply returns its own name.
Intuition
When we need an object that can respond to any method call that doesn't actually exist, we need to think about how JavaScript handles property access and method calls. In JavaScript, calling a method like obj.abc123()
happens in two steps:
- First, the property
abc123
is accessed fromobj
- Then, the result is called as a function with
()
Since we can't possibly define every possible method name ahead of time (there are infinite possibilities), we need a way to dynamically intercept these property accesses and return something callable.
This is where JavaScript's Proxy
comes in. A Proxy
allows us to intercept and customize operations performed on objects, including property access. When we create a proxy with a get
trap, we can control what happens whenever any property is accessed on our object.
The clever part is realizing that when the get
trap is triggered, we receive the property name as a parameter (prop
). Instead of returning a value or undefined, we return a function. This function, when called, simply returns the property name that was originally accessed.
So the flow works like this:
obj.abc123()
triggers theget
trap withprop = "abc123"
- The
get
trap returns a function:() => "abc123"
- The
()
immediately calls this returned function - The function executes and returns
"abc123"
By returning a function from the get
trap that closes over the property name, we create the illusion that every possible method exists on our object, and each one knows its own name.
Solution Approach
The implementation uses JavaScript's Proxy
object to create the infinite-method object. Here's how the solution works step by step:
-
Create a Proxy with an empty target object: The function starts by creating a new
Proxy
with an empty object{}
as the target. The target doesn't need any properties since we're intercepting all property access anyway. -
Define the handler with a
get
trap: The second argument toProxy
is a handler object that defines which operations to intercept. We only need theget
trap, which intercepts property access operations. -
The
get
trap implementation:get: (_, prop) => () => prop.toString()
- The
get
trap receives three parameters, but we only useprop
(the property name being accessed) - The underscore
_
represents the target object, which we don't need - Instead of returning a value directly, it returns an arrow function
() => prop.toString()
- This returned function, when called, converts the property name to a string and returns it
- The
-
Property access flow: When someone accesses a property like
obj.abc123
:- The proxy intercepts this access
- The
get
trap is triggered withprop = "abc123"
- It returns a function that remembers
"abc123"
through closure - When this function is immediately called with
()
, it returns"abc123"
-
Type definition: The function returns
Record<string, () => string>
, indicating an object where:- Keys are strings (any property name)
- Values are functions that return strings
This pattern leverages JavaScript's dynamic nature and the Proxy's ability to intercept operations, creating an object that appears to have infinite methods without actually defining any of them explicitly.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through how the infinite-method object works with a concrete example:
// Create the infinite-method object const obj = createInfiniteObject(); // Call obj.greeting()
When we call obj.greeting()
, here's what happens step by step:
-
Property Access Phase: JavaScript first tries to access the property
greeting
fromobj
- This triggers the Proxy's
get
trap - The
get
trap receivesprop = "greeting"
- Instead of returning
undefined
(sincegreeting
doesn't exist), the trap returns a function:() => "greeting"
- This triggers the Proxy's
-
Function Execution Phase: The parentheses
()
immediately invoke the returned function- The function executes and returns the string
"greeting"
- Result:
obj.greeting()
returns"greeting"
- The function executes and returns the string
Let's trace through another example with multiple calls:
const obj = createInfiniteObject();
console.log(obj.foo()); // "foo"
console.log(obj.bar()); // "bar"
console.log(obj.test123()); // "test123"
For each call:
obj.foo()
:- Get trap intercepts "foo" → returns
() => "foo"
→ executes → returns"foo"
- Get trap intercepts "foo" → returns
obj.bar()
:- Get trap intercepts "bar" → returns
() => "bar"
→ executes → returns"bar"
- Get trap intercepts "bar" → returns
obj.test123()
:- Get trap intercepts "test123" → returns
() => "test123"
→ executes → returns"test123"
- Get trap intercepts "test123" → returns
The key insight is that the Proxy dynamically generates a new function for each property access, and that function captures the property name through closure. This creates the illusion that the object has infinite pre-defined methods, when in reality, each "method" is created on-the-fly during property access.
Solution Implementation
1def create_infinite_object():
2 """
3 Creates an object with infinite properties where each property is a function
4 that returns its own property name as a string.
5
6 Uses a custom class with __getattr__ to dynamically generate methods for any
7 property accessed, eliminating the need to pre-define all possible properties.
8
9 Returns:
10 An object where any property access returns a function that returns the property name
11 """
12
13 class InfiniteObject:
14 def __getattr__(self, prop):
15 """
16 Magic method for property access when attribute is not found
17
18 Args:
19 prop: Property name being accessed
20
21 Returns:
22 A function that returns the property name as a string
23 """
24 # Return a function that returns the property name as a string
25 return lambda: str(prop)
26
27 def __getitem__(self, prop):
28 """
29 Magic method for bracket notation access
30
31 Args:
32 prop: Property name being accessed via bracket notation
33
34 Returns:
35 A function that returns the property name as a string
36 """
37 # Return a function that converts the property name to string
38 return lambda: str(prop)
39
40 return InfiniteObject()
41
42
43# Example usage:
44# obj = create_infinite_object()
45# obj.abc123() # Returns: "abc123"
46# obj.any_property() # Returns: "any_property"
47# obj['12345']() # Returns: "12345"
48
1import java.lang.reflect.InvocationHandler;
2import java.lang.reflect.Method;
3import java.lang.reflect.Proxy;
4import java.util.Map;
5import java.util.concurrent.ConcurrentHashMap;
6import java.util.function.Supplier;
7
8/**
9 * Utility class for creating objects with infinite properties where each property
10 * is a function that returns its own property name as a string.
11 */
12public class InfiniteObjectCreator {
13
14 /**
15 * Interface representing a dynamic object with infinite properties.
16 * Each property is a Supplier<String> that returns the property name.
17 */
18 public interface InfiniteObject {
19 /**
20 * Get a property by name which returns a supplier of the property name.
21 * @param propertyName the name of the property
22 * @return a Supplier that returns the property name as a string
23 */
24 Supplier<String> getProperty(String propertyName);
25 }
26
27 /**
28 * Creates an object with infinite properties where each property is a function
29 * that returns its own property name as a string.
30 *
31 * Uses a dynamic proxy to generate methods for any property accessed,
32 * eliminating the need to pre-define all possible properties.
33 *
34 * @return An InfiniteObject where any property access returns a function that returns the property name
35 */
36 public static Map<String, Supplier<String>> createInfiniteObject() {
37 // Create a custom Map implementation that dynamically generates Supplier functions
38 return new ConcurrentHashMap<String, Supplier<String>>() {
39 /**
40 * Override get method to dynamically create Supplier functions
41 * @param key the property name being accessed
42 * @return a Supplier that returns the property name as a string
43 */
44 @Override
45 public Supplier<String> get(Object key) {
46 // Convert the key to string
47 String propertyName = key.toString();
48
49 // Return a Supplier (equivalent to JavaScript's function) that returns the property name
50 return () -> propertyName;
51 }
52
53 /**
54 * Override containsKey to always return true since we have "infinite" properties
55 * @param key the property name
56 * @return always true
57 */
58 @Override
59 public boolean containsKey(Object key) {
60 return true;
61 }
62 };
63 }
64
65 /**
66 * Alternative implementation using Java Dynamic Proxy for a more direct translation
67 * Creates a proxy object that intercepts method calls and returns the method name
68 *
69 * @return A dynamic proxy object with infinite methods
70 */
71 @SuppressWarnings("unchecked")
72 public static <T> T createInfiniteObjectProxy() {
73 return (T) Proxy.newProxyInstance(
74 InfiniteObjectCreator.class.getClassLoader(),
75 new Class<?>[]{}, // No specific interfaces
76 new InvocationHandler() {
77 /**
78 * Handles method invocations on the proxy instance
79 * @param proxy the proxy instance
80 * @param method the Method instance corresponding to the interface method invoked
81 * @param args the arguments passed to the method
82 * @return a Supplier that returns the method name as a string
83 */
84 @Override
85 public Object invoke(Object proxy, Method method, Object[] args) {
86 // Get the method name being called
87 String methodName = method.getName();
88
89 // Return a Supplier that returns the method name
90 return (Supplier<String>) () -> methodName;
91 }
92 }
93 );
94 }
95
96 /**
97 * Example usage demonstration
98 * @param args command line arguments
99 */
100 public static void main(String[] args) {
101 // Using the Map-based implementation
102 Map<String, Supplier<String>> obj = createInfiniteObject();
103
104 // Example: obj.get("abc123").get() returns "abc123"
105 System.out.println(obj.get("abc123").get()); // Output: "abc123"
106
107 // Example: obj.get("anyProperty").get() returns "anyProperty"
108 System.out.println(obj.get("anyProperty").get()); // Output: "anyProperty"
109
110 // Example: obj.get("12345").get() returns "12345"
111 System.out.println(obj.get("12345").get()); // Output: "12345"
112 }
113}
114
1#include <string>
2#include <functional>
3#include <unordered_map>
4#include <memory>
5
6/**
7 * Class that simulates an object with infinite properties where each property
8 * is a function that returns its own property name as a string.
9 *
10 * Uses operator overloading and dynamic method generation to simulate
11 * the behavior of JavaScript's Proxy pattern.
12 */
13class InfiniteObject {
14private:
15 // Cache for storing generated functions to avoid recreating them
16 mutable std::unordered_map<std::string, std::function<std::string()>> method_cache;
17
18public:
19 /**
20 * Inner class to handle property access and function call chaining
21 */
22 class PropertyAccessor {
23 private:
24 std::string property_name;
25
26 public:
27 /**
28 * Constructor
29 * @param prop_name - The name of the property being accessed
30 */
31 explicit PropertyAccessor(const std::string& prop_name)
32 : property_name(prop_name) {}
33
34 /**
35 * Function call operator to execute the property as a function
36 * @returns The property name as a string
37 */
38 std::string operator()() const {
39 return property_name;
40 }
41 };
42
43 /**
44 * Overloaded subscript operator for array-style property access
45 * @param prop - Property name being accessed
46 * @returns A PropertyAccessor object that can be called as a function
47 */
48 PropertyAccessor operator[](const std::string& prop) const {
49 return PropertyAccessor(prop);
50 }
51
52 /**
53 * Overloaded subscript operator for integer property access
54 * @param prop - Property index being accessed
55 * @returns A PropertyAccessor object that can be called as a function
56 */
57 PropertyAccessor operator[](int prop) const {
58 return PropertyAccessor(std::to_string(prop));
59 }
60};
61
62/**
63 * Creates an InfiniteObject instance with infinite properties where each property
64 * is a function that returns its own property name as a string.
65 *
66 * @returns A shared pointer to an InfiniteObject instance
67 */
68std::shared_ptr<InfiniteObject> createInfiniteObject() {
69 return std::make_shared<InfiniteObject>();
70}
71
72/**
73 * Example usage:
74 * auto obj = createInfiniteObject();
75 * (*obj)["abc123"](); // Returns: "abc123"
76 * (*obj)["anyProperty"](); // Returns: "anyProperty"
77 * (*obj)[12345](); // Returns: "12345"
78 */
79
1/**
2 * Creates an object with infinite properties where each property is a function
3 * that returns its own property name as a string.
4 *
5 * Uses a Proxy to dynamically generate methods for any property accessed,
6 * eliminating the need to pre-define all possible properties.
7 *
8 * @returns A Record object where any property access returns a function that returns the property name
9 */
10function createInfiniteObject(): Record<string, () => string> {
11 return new Proxy(
12 {}, // Target object (empty object as base)
13 {
14 /**
15 * Trap for property access
16 * @param _ - Target object (unused)
17 * @param prop - Property name being accessed
18 * @returns A function that returns the property name as a string
19 */
20 get: (_: {}, prop: string | symbol): (() => string) => {
21 // Return a function that converts the property name to string
22 return (): string => prop.toString();
23 },
24 },
25 );
26}
27
28/**
29 * Example usage:
30 * const obj = createInfiniteObject();
31 * obj['abc123'](); // Returns: "abc123"
32 * obj.anyProperty(); // Returns: "anyProperty"
33 * obj['12345'](); // Returns: "12345"
34 */
35
Time and Space Complexity
Time Complexity: O(1)
The createInfiniteObject()
function creates and returns a Proxy object, which is a constant-time operation. The Proxy's get
trap handler executes in O(1)
time when any property is accessed, as it simply returns a function that converts the property name to a string using prop.toString()
. When the returned function is invoked, the toString()
operation on the property name is also O(1)
for typical property access scenarios.
Space Complexity: O(1)
The space complexity is constant because:
- The Proxy object itself takes a fixed amount of memory regardless of how many properties are accessed
- The empty object
{}
used as the target takes constant space - Each property access creates a new function closure, but these are created on-demand and not stored permanently in the object
- The Proxy doesn't actually store any properties - it dynamically generates responses through the
get
trap - Memory usage doesn't grow with the number of different properties accessed, as no actual properties are being stored in the underlying object
Common Pitfalls
1. Confusion Between Property Access and Method Call
A frequent mistake is forgetting that the returned value is a function that needs to be called. Developers often try to access the property without calling it as a method:
Incorrect:
obj = create_infinite_object()
result = obj.abc123 # This returns a function object, not "abc123"
print(result) # Output: <function InfiniteObject.__getattr__.<locals>.<lambda> at 0x...>
Correct:
obj = create_infinite_object()
result = obj.abc123() # Note the parentheses - calling the function
print(result) # Output: "abc123"
2. Attempting to Access Built-in Methods
The __getattr__
method only intercepts attributes that don't exist. Python's built-in methods and attributes might not behave as expected:
Problem:
obj = create_infinite_object() # These might not return what you expect: obj.__class__() # Won't return "__class__" - this is a real attribute obj.__dict__() # Won't return "__dict__" - this is a real attribute
Solution: Override specific magic methods if you need consistent behavior:
class InfiniteObject:
def __getattr__(self, prop):
return lambda: str(prop)
def __getattribute__(self, prop):
# Intercept ALL attribute access, not just missing ones
if prop.startswith('_'):
# Allow access to special methods for Python internals
return object.__getattribute__(self, prop)
return lambda: str(prop)
3. Type Checking and IDE Autocomplete Issues
Static type checkers and IDEs won't recognize dynamically generated methods, leading to warnings and lack of autocomplete:
Problem:
obj = create_infinite_object() obj.my_method() # IDE shows warning: "Unresolved attribute reference"
Solution: Add type hints to help with static analysis:
from typing import Callable, Any
class InfiniteObject:
def __getattr__(self, prop: str) -> Callable[[], str]:
return lambda: str(prop)
# For better IDE support, you could add a __call__ pattern:
def __call__(self, method_name: str) -> str:
return method_name
4. Nested Property Access Attempts
Developers might expect chaining or nested property access to work:
Problem:
obj = create_infinite_object() # This won't work as expected: result = obj.parent.child() # Error: 'function' object has no attribute 'child'
Solution: If chaining is needed, modify the implementation to return another infinite object:
class InfiniteObject:
def __getattr__(self, prop):
def method(*args, **kwargs):
if args or kwargs:
# If called with arguments, return the property name
return str(prop)
# If called without arguments, return property name
return str(prop)
# Also add the ability to chain
method.__getattr__ = lambda nested_prop: InfiniteObject().__getattr__(f"{prop}.{nested_prop}")
return method
5. Memory Considerations with Closure
Each property access creates a new lambda function, which could lead to memory overhead if many properties are accessed:
Solution: Cache frequently accessed methods or use a single method with parameters:
from functools import lru_cache
class InfiniteObject:
@lru_cache(maxsize=128)
def __getattr__(self, prop):
return lambda: str(prop)
What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!