Implement a squashObjectKeys function
Squashing keys refers to the process of transforming a nested objects into a single-level object by flattening out the keys of the object.
1. Basic implementation
Squash keys for an object and use . as seperator. For array values use index as keys.
import getSquashedKey from './src/utils.js';
function squashObjectKeys(input) { const resultObj = {};
function squashObjectKeys(inputObj, parentKey) { for (const key in inputObj) { // only perform operation for non inherited keys if (Object.hasOwn(inputObj, key)) { const value = inputObj[key];
// utility get new key which is squashed const newKey = getSquashedKey(parentKey, key);
if (typeof value !== 'object') { resultObj[newKey] = value; } else { squashObjectKeys(value, newKey); } } } }
squashObjectKeys(input, null);
return resultObj;}
// Usageconst input = { a: 1, b: 2, c: { d: 12, e: { f: 'Hello World', g: [12, 14] } }};const output = squashObjectKeys(input);
/** * Yields: * { "a": 1, "b": 2, "c.d": 12, "c.e.f": "Hello World", "c.e.g.0": 12, "c.e.g.1": 14 } */console.log(output);
The utility to generate new keys which are squashed can be implemented as follows:
function getSquashedKey(parentKey, currentKey) { // when null, return the same key (for root keys ) if (Object.is(parentKey, null)) { return currentKey; }
return `${parentKey}.${currentKey}`;}
2. With depth implementation
In above example, we squashed all keys for any depth of the object or array. In this case, we should only squash keys until a certain depth of the object.
As most of the implementation remains exactly same, I will highlight only the newer additions made on the utility.
function squashObjectKeys(input, depth) { const resultObj = {};
function squashObjectKeys(inputObj, parentKey, depth) { for (const key in inputObj) { // only perform operation for non inherited keys if (Object.hasOwn(inputObj, key)) { const value = inputObj[key];
// get new key which is squashed const newKey = getSquashedKey(parentKey, key);
if (typeof value !== 'object') { resultObj[newKey] = value; } else { if (depth > 0) { squashObjectKeys(value, newKey, depth - 1); } else { resultObj[key] = value; } } } } }
squashObjectKeys(input, null, depth);
return resultObj;}
// Usageconst input = { a: 1, b: 2, c: { // depth 1 d: 12, e: { // depth 2 f: 'Hello World', g: [12, 14], // depth 3 h: { // depth 3 i: 'Hola!' } } }};const output = squashObjectKeys(input, 2);
/** * Yields: * { "a": 1, "b": 2, "c.d": 12, "c.e.f": "Hello World", "g": [12,14], "h": { "i": "Hola!" }} */console.log(output);
3. With empty keys implementation
Since object keys are strings, there can be cases where some keys maybe be passed as empty spaces. In such cases, we simply need to ignore the key, instead of generating squashed keys with empty spaces in between like const key='a.b.c.''.d'
For this implementation we don’t really need to anything within the squashObjectKeys
function, we can simply alter the getSquashedKey
utility created as such:
function getSquashedKey(parentKey, currentKey) { if (Object.is(parentKey, null)) { return currentKey; }
if (!currentKey || !currentKey.trim().length) { return parentKey; }
return `${parentKey}.${currentKey}`;}
// Output changes// Usageconst input = { a: 1, b: 2, c: { d: 12, '': { f: { '': { h: 16 } } } }};const output = squashObjectKeys(input);
/** * Yields: * { "a": 1, "b": 2, "c.d": 12, "c.f.h": 16 }} */console.log(output);
Further Reading
I strongly encourage you to explore and tackle additional questions in my Recursion Questions for Frontend Interviews blog series.
By doing so, you can enhance your understanding and proficiency with recursion, preparing you to excel in your upcoming frontend interviews.
Wishing you best. Happy Interviewing 🫡.