ServiceNow IC2 | HM Round Interview – JavaScript Deep Flatten + Type Grouping (with a Catch)
Anonymous User
1481
  • Part 1: Main problem + solution
  • Part 2: Follow-up problem (TypeError) + root-cause + fix

💼 HM Round Interview – JavaScript Deep Flatten + Type Grouping (with a Catch)


🔹 Part 1: Main Problem Asked

"You're given a deeply nested array with all kinds of values (numbers, strings, booleans, null, undefined, functions, symbols, objects).
Write a function that:

  • ✅ Deep flattens the array
  • ✅ Groups elements by their JavaScript typeof
  • ✅ Tracks the frequency count of each unique value within that type (This was the followup of the main question)

🔸 Sample Input:

const input = [1, 'a', [true, ['b', undefined], [{} , null]], () => {}, Symbol('x')];

🔸 Expected Output:

{
  number: { '1': 1 },
  string: { 'a': 1, 'b': 1 },
  boolean: { 'true': 1 },
  undefined: { 'undefined': 1 },
  null: { 'null': 1 },
  object: { '[object Object]': 1 },
  function: { '() => {}': 1 },
  symbol: { 'Symbol(x)': 1 }
}

🔹 Part 2: Follow-Up Challenge – Runtime Error & Fix

While implementing this, I hit a TypeError:

Cannot read properties of undefined (reading '1')


🐞 Root Cause:

if (!result[key]) {
  result[key] = {};
}

❌ Incorrect check — I was initializing based on key (the value),
but should have checked result[type] instead (the group).


✅ Corrected Code:

if (!result[type]) {
  result[type] = {};
}

🔍 Bonus Follow-Ups Asked:

  • Why does typeof null === 'object'?
    → Legacy bug. I handled it using: item === null ? 'null' : typeof item.

  • How did you stringify symbols safely for keys?
    → Used item.toString() for Symbol(x) values.

  • What if you needed to keep raw values instead of string keys?
    → Suggested using a Map or { value, count } objects instead of stringified keys.


✅ Outcome:

Handled edge cases, debugging, and recursion with safe typing and clean grouping logic.


Both versions of the solution:


✅ 1. With Frequency Count

function deepFlattenAndGroupByTypeWithFrequency(input) {
  const result = {};

  function recurse(arr) {
    for (const item of arr) {
      if (Array.isArray(item)) {
        recurse(item); // handle nested arrays
      } else {
        const type = item === null ? 'null' : typeof item;
        const key = typeof item === 'symbol' ? item.toString() : String(item);

        if (!result[type]) {
          result[type] = {};
        }

        result[type][key] = (result[type][key] || 0) + 1;
      }
    }
  }

  recurse(input);
  return result;
}

🔸 Output Example:

{
  number: { '1': 2 },
  string: { 'a': 2, 'b': 1 },
  boolean: { 'true': 2 },
  undefined: { 'undefined': 1 },
  null: { 'null': 1 },
  symbol: { 'Symbol(x)': 2 }
}

✅ 2. Without Frequency Count (Just grouping by type)

function deepFlattenAndGroupByType(input) {
  const result = {};

  function recurse(arr) {
    for (const item of arr) {
      if (Array.isArray(item)) {
        recurse(item);
      } else {
        const type = item === null ? 'null' : typeof item;

        if (!result[type]) {
          result[type] = [];
        }

        result[type].push(item);
      }
    }
  }

  recurse(input);
  return result;
}

🔸 Output Example:

{
  number: [1, 1],
  string: ['a', 'a', 'b'],
  boolean: [true, true],
  undefined: [undefined],
  null: [null],
  symbol: [Symbol('x'), Symbol('x')]
}

Comments (6)