Facebook Pixel

2675. Array of Objects to Matrix 🔒

Problem Description

You need to convert an array of objects into a matrix (2D array) representation.

The input arr is an array where each element can be:

  • An object (potentially with nested objects/arrays)
  • An array (potentially with nested objects/arrays)
  • Primitive values (numbers, strings, booleans, null)

The output matrix should be structured as follows:

First Row (Headers):

  • Contains column names derived from all unique keys found across all objects in the array
  • For nested objects, the column name is the path to that value, with levels separated by dots (.)
  • For example, if an object has {user: {name: "John"}}, the column would be "user.name"
  • Column names must be sorted in lexicographical (alphabetical) order

Data Rows:

  • Each subsequent row represents one object from the input array
  • Values are placed in their corresponding columns based on the headers
  • If an object doesn't have a value for a particular column, use an empty string ("")

Example: If the input is:

[
  {a: 1, b: {c: 2}},
  {a: 3, b: {c: 4, d: 5}}
]

The output matrix would be:

[
  ["a", "b.c", "b.d"],  // Headers (sorted)
  [1, 2, ""],           // First object (no b.d)
  [3, 4, 5]             // Second object
]

The challenge involves:

  1. Recursively traversing nested structures to extract all possible paths
  2. Collecting all unique column names across all objects
  3. Mapping each object's values to the correct columns
  4. Handling missing values appropriately
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that we need to flatten nested objects into a single-level representation where each nested path becomes a unique key. Think of it like converting a tree structure into a flat table format.

To achieve this transformation, we can break down the problem into three main steps:

  1. Flatten each object into key-value pairs: For nested structures, we need to recursively traverse and create dot-separated paths. For example, {a: {b: 1}} becomes {"a.b": 1}. This flattening process naturally handles any level of nesting by building up the path as we go deeper.

  2. Collect all unique column names: Once we have flattened representations of all objects, we can gather all unique keys (paths) that appear across any object. These become our column headers. Sorting them lexicographically ensures consistent column ordering.

  3. Build the matrix row by row: For each original object, we create a row by looking up values for each column. If a particular path doesn't exist in that object, we fill it with an empty string.

The recursive depth-first search (DFS) approach works well here because:

  • When we encounter a primitive value (number, string, boolean, null), we've reached a leaf node and can return the key-value pair
  • When we encounter an object or array, we need to go deeper, appending the current key to the path
  • By flattening the results at each level, we ultimately get a flat array of key-value pairs for each object

This approach elegantly handles the variable structure of input objects - some might be deeply nested while others are flat, some might have certain paths while others don't. The flattening strategy normalizes everything into a consistent format that can be easily converted into a matrix.

Solution Approach

The implementation uses a recursive DFS (Depth-First Search) approach to flatten nested objects and then constructs the matrix from the flattened data.

Step 1: Define the DFS function for flattening

The dfs function takes two parameters:

  • key: The current path as a string (dot-separated)
  • obj: The current object/value being processed
const dfs = (key: string, obj: any) => {
    // Base case: primitive values
    if (typeof obj === 'number' || typeof obj === 'string' || 
        typeof obj === 'boolean' || obj === null) {
        return { [key]: obj };
    }
    // Recursive case: objects/arrays
    const res: any[] = [];
    for (const [k, v] of Object.entries(obj)) {
        const newKey = key ? `${key}.${k}` : `${k}`;
        res.push(dfs(newKey, v));
    }
    return res.flat();
};

For primitive values, we return an object with the accumulated path as the key. For objects/arrays, we iterate through each property, build the new path by appending the current key with a dot separator, and recursively process each value. The flat() method ensures we get a single-level array of key-value objects.

Step 2: Flatten all objects in the input array

const kv = arr.map(obj => dfs('', obj));

We map over the input array, calling dfs with an empty initial key for each object. This gives us an array where each element is an array of flattened key-value pairs.

Step 3: Extract and sort unique column names

const keys = [
    ...new Set(
        kv.flat()
          .map(obj => Object.keys(obj))
          .flat()
    )
].sort();

We flatten the array of key-value pairs, extract all keys, use a Set to get unique values, and sort them lexicographically. These become our column headers.

Step 4: Build the matrix

const ans: any[] = [keys];  // Start with headers
for (const row of kv) {
    const newRow: any[] = [];
    for (const key of keys) {
        const v = row.find(r => r.hasOwnProperty(key))?.[key];
        newRow.push(v === undefined ? '' : v);
    }
    ans.push(newRow);
}

We initialize the result matrix with the headers as the first row. Then for each original object (represented by its flattened key-value pairs), we create a new row by:

  • Iterating through each column name in order
  • Finding the corresponding value in the flattened representation
  • Using an empty string if the value doesn't exist
  • Adding the completed row to the matrix

The use of hasOwnProperty ensures we correctly handle cases where a value might be undefined (which should be preserved) versus not existing at all (which should become an empty string).

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 a small example to illustrate the solution approach:

Input:

[
  {name: "Alice", age: 30},
  {name: "Bob", details: {age: 25, city: "NYC"}}
]

Step 1: Flatten each object using DFS

For the first object {name: "Alice", age: 30}:

  • Call dfs("", {name: "Alice", age: 30})
  • Process name: "Alice"dfs("name", "Alice") returns {name: "Alice"}
  • Process age: 30dfs("age", 30) returns {age: 30}
  • Result: [{name: "Alice"}, {age: 30}]

For the second object {name: "Bob", details: {age: 25, city: "NYC"}}:

  • Call dfs("", {name: "Bob", details: {...}})
  • Process name: "Bob" → returns {name: "Bob"}
  • Process details: {age: 25, city: "NYC"}:
    • Call dfs("details", {age: 25, city: "NYC"})
    • Process age: 25dfs("details.age", 25) returns {details.age: 25}
    • Process city: "NYC"dfs("details.city", "NYC") returns {details.city: "NYC"}
  • Result: [{name: "Bob"}, {details.age: 25}, {details.city: "NYC"}]

After flattening, we have:

kv = [
  [{name: "Alice"}, {age: 30}],
  [{name: "Bob"}, {details.age: 25}, {details.city: "NYC"}]
]

Step 2: Extract and sort unique column names

Collect all keys from flattened objects:

  • From first object: ["name", "age"]
  • From second object: ["name", "details.age", "details.city"]
  • Unique keys: ["name", "age", "details.age", "details.city"]
  • Sorted: ["age", "details.age", "details.city", "name"]

Step 3: Build the matrix

Start with headers:

[["age", "details.age", "details.city", "name"]]

For first object's row:

  • age: found → 30
  • details.age: not found → ""
  • details.city: not found → ""
  • name: found → "Alice"
  • Row: [30, "", "", "Alice"]

For second object's row:

  • age: not found → ""
  • details.age: found → 25
  • details.city: found → "NYC"
  • name: found → "Bob"
  • Row: ["", 25, "NYC", "Bob"]

Final Output:

[
  ["age", "details.age", "details.city", "name"],
  [30, "", "", "Alice"],
  ["", 25, "NYC", "Bob"]
]

The matrix successfully represents both objects with a consistent column structure, filling in empty strings where values don't exist.

Solution Implementation

1def jsonToMatrix(arr):
2    """
3    Converts an array of JSON objects into a matrix format with flattened keys
4  
5    Args:
6        arr: List of dictionaries to be converted into matrix format
7      
8    Returns:
9        Matrix where first row contains sorted column headers and subsequent rows contain values
10    """
11  
12    def flatten_object(key, obj):
13        """
14        Recursively flattens nested objects into key-value pairs with dot notation
15      
16        Args:
17            key: Current key path (e.g., "parent.child")
18            obj: Current object or value being processed
19          
20        Returns:
21            Flattened dictionary(s) with dot-notation keys
22        """
23        # Base case: primitive values or None
24        if (isinstance(obj, (int, float, str, bool)) or obj is None):
25            return {key: obj}
26      
27        # Recursive case: process nested dictionaries
28        flattened = []
29        for property_key, property_value in obj.items():
30            # Build new key path with dot notation
31            new_key_path = f"{key}.{property_key}" if key else property_key
32            result = flatten_object(new_key_path, property_value)
33          
34            # Handle both single dict and list of dicts
35            if isinstance(result, list):
36                flattened.extend(result)
37            else:
38                flattened.append(result)
39      
40        # Return flattened list of dictionaries
41        return flattened
42  
43    # Convert each object in the array to flattened key-value pairs
44    flattened_rows = []
45    for obj in arr:
46        flattened = flatten_object('', obj)
47        # Ensure result is always a list
48        if isinstance(flattened, dict):
49            flattened_rows.append([flattened])
50        else:
51            flattened_rows.append(flattened)
52  
53    # Extract all unique keys from flattened objects and sort them
54    unique_keys_set = set()
55    for flattened_row in flattened_rows:
56        for flat_object in flattened_row:
57            unique_keys_set.update(flat_object.keys())
58  
59    unique_keys = sorted(list(unique_keys_set))
60  
61    # Build the result matrix with headers as first row
62    result_matrix = [unique_keys]
63  
64    # Process each row to align values with column headers
65    for flattened_row in flattened_rows:
66        matrix_row = []
67      
68        for column_key in unique_keys:
69            # Find the object containing the current key
70            matching_object = None
71            for flat_object in flattened_row:
72                if column_key in flat_object:
73                    matching_object = flat_object
74                    break
75          
76            # Extract value or use empty string if key doesn't exist
77            if matching_object and column_key in matching_object:
78                cell_value = matching_object[column_key]
79            else:
80                cell_value = ''
81          
82            matrix_row.append(cell_value)
83      
84        result_matrix.append(matrix_row)
85  
86    return result_matrix
87
1import java.util.*;
2import java.util.stream.Collectors;
3
4public class JsonToMatrixConverter {
5  
6    /**
7     * Converts an array of JSON objects into a matrix format with flattened keys
8     * @param arr - List of maps representing JSON objects to be converted into matrix format
9     * @return Matrix where first row contains sorted column headers and subsequent rows contain values
10     */
11    public static List<List<Object>> jsonToMatrix(List<Map<String, Object>> arr) {
12        // Convert each object in the array to flattened key-value pairs
13        List<List<Map<String, Object>>> flattenedRows = new ArrayList<>();
14        for (Map<String, Object> object : arr) {
15            flattenedRows.add(flattenObject("", object));
16        }
17      
18        // Extract all unique keys from flattened objects and sort them
19        Set<String> uniqueKeysSet = new TreeSet<>(); // TreeSet maintains sorted order
20        for (List<Map<String, Object>> flattenedRow : flattenedRows) {
21            for (Map<String, Object> flatObject : flattenedRow) {
22                uniqueKeysSet.addAll(flatObject.keySet());
23            }
24        }
25        List<String> uniqueKeys = new ArrayList<>(uniqueKeysSet);
26      
27        // Build the result matrix with headers as first row
28        List<List<Object>> resultMatrix = new ArrayList<>();
29        // Add header row
30        resultMatrix.add(new ArrayList<>(uniqueKeys));
31      
32        // Process each row to align values with column headers
33        for (List<Map<String, Object>> flattenedRow : flattenedRows) {
34            List<Object> matrixRow = new ArrayList<>();
35          
36            for (String columnKey : uniqueKeys) {
37                // Find the object containing the current key
38                Map<String, Object> matchingObject = null;
39                for (Map<String, Object> flatObject : flattenedRow) {
40                    if (flatObject.containsKey(columnKey)) {
41                        matchingObject = flatObject;
42                        break;
43                    }
44                }
45              
46                // Extract value or use empty string if key doesn't exist
47                Object cellValue = matchingObject != null ? matchingObject.get(columnKey) : "";
48                matrixRow.add(cellValue);
49            }
50          
51            resultMatrix.add(matrixRow);
52        }
53      
54        return resultMatrix;
55    }
56  
57    /**
58     * Recursively flattens nested objects into key-value pairs with dot notation
59     * @param key - Current key path (e.g., "parent.child")
60     * @param obj - Current object or value being processed
61     * @return List of flattened objects with dot-notation keys
62     */
63    private static List<Map<String, Object>> flattenObject(String key, Object obj) {
64        // Base case: primitive values or null
65        if (obj == null || 
66            obj instanceof Number || 
67            obj instanceof String || 
68            obj instanceof Boolean) {
69            // Return single map with the key-value pair
70            Map<String, Object> result = new HashMap<>();
71            result.put(key, obj);
72            return Arrays.asList(result);
73        }
74      
75        // Recursive case: process nested objects (Map)
76        if (obj instanceof Map) {
77            List<Map<String, Object>> flattened = new ArrayList<>();
78            Map<String, Object> mapObj = (Map<String, Object>) obj;
79          
80            for (Map.Entry<String, Object> entry : mapObj.entrySet()) {
81                String propertyKey = entry.getKey();
82                Object propertyValue = entry.getValue();
83              
84                // Build new key path with dot notation
85                String newKeyPath = key.isEmpty() ? propertyKey : key + "." + propertyKey;
86              
87                // Recursively flatten and add all results
88                flattened.addAll(flattenObject(newKeyPath, propertyValue));
89            }
90          
91            return flattened;
92        }
93      
94        // Handle unexpected types by returning empty list
95        return new ArrayList<>();
96    }
97}
98
1#include <vector>
2#include <string>
3#include <unordered_map>
4#include <set>
5#include <algorithm>
6#include <variant>
7#include <memory>
8
9using namespace std;
10
11// Define a variant type to hold different JSON value types
12using JsonValue = variant<nullptr_t, bool, int, double, string>;
13
14// Define a JSON object as a map of string keys to either values or nested objects
15class JsonObject {
16public:
17    unordered_map<string, variant<JsonValue, shared_ptr<JsonObject>>> data;
18};
19
20/**
21 * Converts an array of JSON objects into a matrix format with flattened keys
22 * @param arr - Array of objects to be converted into matrix format
23 * @returns Matrix where first row contains sorted column headers and subsequent rows contain values
24 */
25vector<vector<variant<string, int, double, bool, nullptr_t>>> jsonToMatrix(vector<shared_ptr<JsonObject>> arr) {
26    // Type alias for the flattened key-value pairs
27    using FlattenedObject = unordered_map<string, JsonValue>;
28  
29    /**
30     * Recursively flattens nested objects into key-value pairs with dot notation
31     * @param key - Current key path (e.g., "parent.child")
32     * @param obj - Current object or value being processed
33     * @returns Flattened object(s) with dot-notation keys
34     */
35    auto flattenObject = [](const string& key, const variant<JsonValue, shared_ptr<JsonObject>>& obj) -> vector<FlattenedObject> {
36        // Lambda for recursive flattening
37        function<vector<FlattenedObject>(const string&, const variant<JsonValue, shared_ptr<JsonObject>>&)> flatten;
38      
39        flatten = [&flatten](const string& currentKey, const variant<JsonValue, shared_ptr<JsonObject>>& currentObj) -> vector<FlattenedObject> {
40            // Check if it's a primitive value or null
41            if (holds_alternative<JsonValue>(currentObj)) {
42                FlattenedObject result;
43                result[currentKey] = get<JsonValue>(currentObj);
44                return {result};
45            }
46          
47            // Process nested object
48            vector<FlattenedObject> flattened;
49            auto jsonObj = get<shared_ptr<JsonObject>>(currentObj);
50          
51            for (const auto& [propertyKey, propertyValue] : jsonObj->data) {
52                // Build new key path with dot notation
53                string newKeyPath = currentKey.empty() ? propertyKey : currentKey + "." + propertyKey;
54              
55                // Recursively flatten and collect results
56                auto nestedResults = flatten(newKeyPath, propertyValue);
57                flattened.insert(flattened.end(), nestedResults.begin(), nestedResults.end());
58            }
59          
60            // Merge all flattened objects into a single level
61            if (flattened.empty()) {
62                return {FlattenedObject()};
63            }
64          
65            FlattenedObject merged;
66            for (const auto& flatObj : flattened) {
67                for (const auto& [k, v] : flatObj) {
68                    merged[k] = v;
69                }
70            }
71            return {merged};
72        };
73      
74        return flatten(key, obj);
75    };
76  
77    // Convert each object in the array to flattened key-value pairs
78    vector<vector<FlattenedObject>> flattenedRows;
79    for (const auto& object : arr) {
80        variant<JsonValue, shared_ptr<JsonObject>> objVariant = object;
81        flattenedRows.push_back(flattenObject("", objVariant));
82    }
83  
84    // Extract all unique keys from flattened objects
85    set<string> uniqueKeysSet;
86    for (const auto& row : flattenedRows) {
87        for (const auto& flatObj : row) {
88            for (const auto& [key, value] : flatObj) {
89                uniqueKeysSet.insert(key);
90            }
91        }
92    }
93  
94    // Convert set to sorted vector
95    vector<string> uniqueKeys(uniqueKeysSet.begin(), uniqueKeysSet.end());
96    sort(uniqueKeys.begin(), uniqueKeys.end());
97  
98    // Build the result matrix with headers as first row
99    vector<vector<variant<string, int, double, bool, nullptr_t>>> resultMatrix;
100  
101    // Add header row
102    vector<variant<string, int, double, bool, nullptr_t>> headerRow;
103    for (const auto& key : uniqueKeys) {
104        headerRow.push_back(key);
105    }
106    resultMatrix.push_back(headerRow);
107  
108    // Process each row to align values with column headers
109    for (const auto& flattenedRow : flattenedRows) {
110        vector<variant<string, int, double, bool, nullptr_t>> matrixRow;
111      
112        // Merge all flattened objects in the row
113        FlattenedObject mergedRow;
114        for (const auto& flatObj : flattenedRow) {
115            for (const auto& [k, v] : flatObj) {
116                mergedRow[k] = v;
117            }
118        }
119      
120        // Fill values for each column
121        for (const auto& columnKey : uniqueKeys) {
122            // Find if the current key exists in the merged row
123            if (mergedRow.find(columnKey) != mergedRow.end()) {
124                // Convert JsonValue to the appropriate variant type
125                JsonValue val = mergedRow[columnKey];
126                if (holds_alternative<nullptr_t>(val)) {
127                    matrixRow.push_back(nullptr);
128                } else if (holds_alternative<bool>(val)) {
129                    matrixRow.push_back(get<bool>(val));
130                } else if (holds_alternative<int>(val)) {
131                    matrixRow.push_back(get<int>(val));
132                } else if (holds_alternative<double>(val)) {
133                    matrixRow.push_back(get<double>(val));
134                } else if (holds_alternative<string>(val)) {
135                    matrixRow.push_back(get<string>(val));
136                }
137            } else {
138                // Use empty string if key doesn't exist
139                matrixRow.push_back(string(""));
140            }
141        }
142      
143        resultMatrix.push_back(matrixRow);
144    }
145  
146    return resultMatrix;
147}
148
1/**
2 * Converts an array of JSON objects into a matrix format with flattened keys
3 * @param arr - Array of objects to be converted into matrix format
4 * @returns Matrix where first row contains sorted column headers and subsequent rows contain values
5 */
6function jsonToMatrix(arr: any[]): (string | number | boolean | null)[][] {
7    /**
8     * Recursively flattens nested objects into key-value pairs with dot notation
9     * @param key - Current key path (e.g., "parent.child")
10     * @param obj - Current object or value being processed
11     * @returns Flattened object(s) with dot-notation keys
12     */
13    const flattenObject = (key: string, obj: any): any => {
14        // Base case: primitive values or null
15        if (
16            typeof obj === 'number' ||
17            typeof obj === 'string' ||
18            typeof obj === 'boolean' ||
19            obj === null
20        ) {
21            return { [key]: obj };
22        }
23      
24        // Recursive case: process nested objects
25        const flattened: any[] = [];
26        for (const [propertyKey, propertyValue] of Object.entries(obj)) {
27            // Build new key path with dot notation
28            const newKeyPath = key ? `${key}.${propertyKey}` : propertyKey;
29            flattened.push(flattenObject(newKeyPath, propertyValue));
30        }
31      
32        // Flatten array of results into single level
33        return flattened.flat();
34    };
35
36    // Convert each object in the array to flattened key-value pairs
37    const flattenedRows = arr.map(object => flattenObject('', object));
38  
39    // Extract all unique keys from flattened objects and sort them
40    const uniqueKeys = [
41        ...new Set(
42            flattenedRows
43                .flat()
44                .map(flatObject => Object.keys(flatObject))
45                .flat()
46        )
47    ].sort();
48  
49    // Build the result matrix with headers as first row
50    const resultMatrix: any[][] = [uniqueKeys];
51  
52    // Process each row to align values with column headers
53    for (const flattenedRow of flattenedRows) {
54        const matrixRow: any[] = [];
55      
56        for (const columnKey of uniqueKeys) {
57            // Find the object containing the current key
58            const matchingObject = flattenedRow.find(
59                flatObject => flatObject.hasOwnProperty(columnKey)
60            );
61          
62            // Extract value or use empty string if key doesn't exist
63            const cellValue = matchingObject?.[columnKey];
64            matrixRow.push(cellValue === undefined ? '' : cellValue);
65        }
66      
67        resultMatrix.push(matrixRow);
68    }
69  
70    return resultMatrix;
71}
72

Time and Space Complexity

Time Complexity: O(n * m * d + n * k^2)

Where:

  • n = number of objects in the input array
  • m = average number of key-value pairs per object (including nested)
  • d = maximum depth of nesting
  • k = total number of unique keys across all objects

Breaking down the operations:

  1. The dfs function traverses each object recursively: O(m * d) per object
  2. Mapping dfs over all objects: O(n * m * d)
  3. Extracting and deduplicating keys: O(n * m) for extraction, O(k log k) for sorting
  4. Building the result matrix: O(n * k) iterations, with each iteration doing a find operation that costs O(k) in worst case

The dominant term is O(n * k^2) from the nested loop with the find operation, though in practice if objects are sparse, this could be closer to O(n * m * d + n * k).

Space Complexity: O(n * m * d + k)

Where the space is used for:

  1. Recursive call stack for dfs: O(d) maximum depth
  2. Flattened key-value pairs stored in kv: O(n * m) total pairs
  3. Unique keys array: O(k)
  4. Result matrix: O(n * k) for the final answer

The dominant term is O(n * k) for storing the final matrix, though the intermediate storage of flattened objects also contributes significantly as O(n * m).

Common Pitfalls

1. Incorrect Handling of Array Elements Within Objects

One major pitfall is not properly handling arrays that appear as values within objects. The current implementation treats arrays like objects (iterating over their indices as keys), which creates column names like "items.0", "items.1", etc., instead of properly handling array elements.

Problem Example:

arr = [
    {"id": 1, "items": ["a", "b"]},
    {"id": 2, "items": ["c", "d", "e"]}
]

Current output would create columns: ["id", "items.0", "items.1", "items.2"]

Solution: Add explicit array detection and handling:

def flatten_object(key, obj):
    # Handle arrays differently from objects
    if isinstance(obj, list):
        flattened = []
        for index, item in enumerate(obj):
            new_key_path = f"{key}.{index}" if key else str(index)
            result = flatten_object(new_key_path, item)
            if isinstance(result, list):
                flattened.extend(result)
            else:
                flattened.append(result)
        return flattened
  
    # Handle dictionaries
    if isinstance(obj, dict):
        flattened = []
        for property_key, property_value in obj.items():
            new_key_path = f"{key}.{property_key}" if key else property_key
            result = flatten_object(new_key_path, property_value)
            if isinstance(result, list):
                flattened.extend(result)
            else:
                flattened.append(result)
        return flattened
  
    # Base case: primitive values
    return {key: obj}

2. Loss of Data When Multiple Values Map to Same Key

When flattening nested structures, if an object contains both a value and nested properties at the same level, data can be lost because the flattened representation can't distinguish between them.

Problem Example:

arr = [
    {"a": {"b": 1, "c": {"d": 2}}},
    {"a": {"b": 3, "c": 4}}  # 'c' is sometimes a value, sometimes an object
]

This creates inconsistent structures where "a.c" and "a.c.d" both exist, but "a.c" might be overwritten.

Solution: Implement validation or use a merging strategy:

def flatten_object(key, obj, flattened_dict=None):
    if flattened_dict is None:
        flattened_dict = {}
  
    if isinstance(obj, dict):
        for k, v in obj.items():
            new_key = f"{key}.{k}" if key else k
            flatten_object(new_key, v, flattened_dict)
    elif isinstance(obj, list):
        for i, item in enumerate(obj):
            new_key = f"{key}.{i}" if key else str(i)
            flatten_object(new_key, item, flattened_dict)
    else:
        # Check for conflicts before assignment
        if key in flattened_dict:
            # Handle conflict (could log, merge, or raise error)
            pass
        flattened_dict[key] = obj
  
    return flattened_dict

3. Memory Inefficiency with Deep Nesting

The recursive approach with list concatenation (extend() and append()) creates many intermediate lists, which can be memory-intensive for deeply nested structures.

Solution: Use a single accumulator dictionary passed through recursion:

def jsonToMatrix(arr):
    def flatten_object(obj, prefix='', result=None):
        if result is None:
            result = {}
      
        if isinstance(obj, dict):
            for key, value in obj.items():
                new_key = f"{prefix}.{key}" if prefix else key
                if isinstance(value, (dict, list)):
                    flatten_object(value, new_key, result)
                else:
                    result[new_key] = value
        elif isinstance(obj, list):
            for i, item in enumerate(obj):
                new_key = f"{prefix}.{i}" if prefix else str(i)
                if isinstance(item, (dict, list)):
                    flatten_object(item, new_key, result)
                else:
                    result[new_key] = item
        else:
            result[prefix] = obj
      
        return result
  
    # Flatten all objects
    flattened_rows = [flatten_object(obj) for obj in arr]
  
    # Get unique keys
    all_keys = set()
    for row in flattened_rows:
        all_keys.update(row.keys())
    sorted_keys = sorted(all_keys)
  
    # Build matrix
    matrix = [sorted_keys]
    for row in flattened_rows:
        matrix.append([row.get(key, '') for key in sorted_keys])
  
    return matrix

This approach uses a single dictionary per object instead of lists of dictionaries, significantly reducing memory overhead and improving performance.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input 56?

1KEYBOARD = {
2    '2': 'abc',
3    '3': 'def',
4    '4': 'ghi',
5    '5': 'jkl',
6    '6': 'mno',
7    '7': 'pqrs',
8    '8': 'tuv',
9    '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13    def dfs(path, res):
14        if len(path) == len(digits):
15            res.append(''.join(path))
16            return
17
18        next_number = digits[len(path)]
19        for letter in KEYBOARD[next_number]:
20            path.append(letter)
21            dfs(path, res)
22            path.pop()
23
24    res = []
25    dfs([], res)
26    return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2    '2', "abc".toCharArray(),
3    '3', "def".toCharArray(),
4    '4', "ghi".toCharArray(),
5    '5', "jkl".toCharArray(),
6    '6', "mno".toCharArray(),
7    '7', "pqrs".toCharArray(),
8    '8', "tuv".toCharArray(),
9    '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13    List<String> res = new ArrayList<>();
14    dfs(new StringBuilder(), res, digits.toCharArray());
15    return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19    if (path.length() == digits.length) {
20        res.add(path.toString());
21        return;
22    }
23    char next_digit = digits[path.length()];
24    for (char letter : KEYBOARD.get(next_digit)) {
25        path.append(letter);
26        dfs(path, res, digits);
27        path.deleteCharAt(path.length() - 1);
28    }
29}
30
1const KEYBOARD = {
2    '2': 'abc',
3    '3': 'def',
4    '4': 'ghi',
5    '5': 'jkl',
6    '6': 'mno',
7    '7': 'pqrs',
8    '8': 'tuv',
9    '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13    let res = [];
14    dfs(digits, [], res);
15    return res;
16}
17
18function dfs(digits, path, res) {
19    if (path.length === digits.length) {
20        res.push(path.join(''));
21        return;
22    }
23    let next_number = digits.charAt(path.length);
24    for (let letter of KEYBOARD[next_number]) {
25        path.push(letter);
26        dfs(digits, path, res);
27        path.pop();
28    }
29}
30

Recommended Readings

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

Load More