Object flattening

Benchmark created on


Setup



const obj = {
  id: 1,
  text: "Text 1",
  items: [
    {
      id: 11,
      text: "Text 11",
      items: [
        {
          id: 111,
          text: "Text 111",
          items: null,
          isValid: false,
          isAnother: true,
        },
        {
          id: 111,
          text: "Text 111",
          items: null,
          isValid: false,
          isAnother: true,
        },
        {
          id: 111,
          text: "Text 111",
          items: null,
          isValid: false,
          isAnother: true,
        },
      ],
    },
    {
      id: 11,
      text: "Text 11",
      items: [
        {
          id: 111,
          text: "Text 111",
          items: null,
          isValid: false,
          isAnother: true,
        },
      ],
    },
    {
      id: 11,
      text: "Text 11",
      items: [
        {
          id: 111,
          text: "Text 111",
          items: null,
          isValid: false,
          isAnother: true,
        },
      ],
    },

  ],
};

function flatten1(result, input, routeBase = "") {
  switch (typeof input) {
    case "object":
      if (Array.isArray(input)) {
        input.forEach((row, index) =>
          flatten1(
            result,
            row,
            routeBase ? `${routeBase}[${index}]` : `[${index}]`
          )
        );
      } else {
        if (input === null || input === undefined) {
          result[routeBase ? `${routeBase}` : ""] = input;
        } else {
          for (const prop in input) {
            if (input.hasOwnProperty(prop)) {
              flatten1(
                result,
                input[prop],
                routeBase ? `${routeBase}.${prop}` : prop
              );
            }
          }
        }
      }
      break;
    default:
      result[routeBase ? `${routeBase}` : ""] = input;
      break;
  }
}

function flatten2(result, input, routeBase = "") {
  if (input === null || input === undefined || typeof input !== "object") {
    result[routeBase] = input;
    return;
  }

  if (Array.isArray(input)) {
    for (const [index, row] of input.entries()) {
      flatten2(result, row, `${routeBase}[${index}]`);
    }
  } else {
    for (const [prop, value] of Object.entries(input)) {
      // no need to check hasOwnProperty, because we are using Object.entries
      flatten2(result, value, routeBase ? `${routeBase}.${prop}` : prop);
    }
  }
}

function flatten3(result, input, routeBase = "") {
  if (input === null || typeof input !== "object") {
    result[routeBase] = input;
    return;
  }

  if (Array.isArray(input)) {
    const base = routeBase ? routeBase : ""; // Avoid repeating routeBase ? routeBase inside loop
    for (let i = 0; i < input.length; i++) {
      flatten3(result, input[i], `${base}[${i}]`);
    }
  } else {
    const base = routeBase ? `${routeBase}.` : ""; // Avoid repeating the ternary inside the loop
    for (const [prop, value] of Object.entries(input)) {
      flatten3(result, value, `${base}${prop}`);
    }
  }
}

function flatten4(result, input, routeBase = "") {
  if (input === null || typeof input !== "object") {
    result[routeBase] = input;
    return;
  }

  if (Array.isArray(input)) {
    for (let i = 0; i < input.length; i++) {
      flatten4(result, input[i], `${routeBase}[${i}]`);
    }
  } else {
    const base = routeBase ? `${routeBase}.` : "";
    for (const [prop, value] of Object.entries(input)) {
      flatten4(result, value, `${base}${prop}`);
    }
  }
}

function flatten5(result, input, routeBase = []) {
  if (input === null || typeof input !== "object") {
    result[routeBase.join('')] = input;
    return;
  }

  if (Array.isArray(input)) {
    for (let i = 0; i < input.length; i++) {
      routeBase.push(`[${i}]`);
      flatten5(result, input[i], routeBase);
      routeBase.pop();
    }
  } else {
    for (const [prop, value] of Object.entries(input)) {
      routeBase.push(routeBase.length ? `.${prop}` : prop);
      flatten5(result, value, routeBase);
      routeBase.pop();
    }
  }
}

function flatten6(result, input) {
  const stack = [{ input, routeBase: "" }];

  while (stack.length > 0) {
    const { input, routeBase } = stack.pop();

    if (input === null || typeof input !== "object") {
      result[routeBase] = input;
      continue;
    }

    if (Array.isArray(input)) {
      for (let i = input.length - 1; i >= 0; i--) {
        stack.push({ input: input[i], routeBase: `${routeBase}[${i}]` });
      }
    } else {
      const base = routeBase ? `${routeBase}.` : "";
      for (const [prop, value] of Object.entries(input)) {
        stack.push({ input: value, routeBase: `${base}${prop}` });
      }
    }
  }
}

function flatten7(result, input, routeBase = '') {
    if (input === null || input === undefined || typeof input !== 'object') {
      result[routeBase] = input;
      return;
    }

    if (Array.isArray(input)) {
      for (const [index, row] of input.entries()) {
        flatten7(result, row, `${routeBase}[${index}]`);
      }
    } else {
      const base = routeBase ? `${routeBase}.` : '';
      for (const [prop, value] of Object.entries(input)) {
        // no need to check hasOwnProperty, because we are using Object.entries
        flatten7(result, value, `${base}${prop}`);
      }
    }
}

function flatten8(result, input, routeBase = '') {
  switch (typeof input) {
    case 'object':
      if (Array.isArray(input)) {
        input.forEach((row, index) => flatten8(result, row, `${routeBase}[${index}]`));
      } else {
        if (input === null || input === undefined) {
          result[routeBase] = input;
        } else {
          for (const prop in input) {
            const base = routeBase ? `${routeBase}.` : '';
            if (input.hasOwnProperty(prop)) {
              flatten8(result, input[prop], `${base}${prop}`);
            }
          }
        }
      }
      break;
    default:
      result[routeBase] = input;
      break;
  }
}

Test runner

Ready to run.

Testing in
TestOps/sec
flatten #1
result = {}
flatten1(result, obj)
ready
flatten #2
result = {}
flatten2(result, obj)
ready
flatten #3
result = {}
flatten3(result, obj)
ready
flatten #4
result = {}
flatten4(result, obj)
ready
flatten #5
result = {}
flatten5(result, obj)
ready
flatten #6
result = {}
flatten6(result, obj)
ready
flatten #7
result = {}
flatten7(result, obj)
ready
flatten #8
result = {}
flatten8(result, obj)
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.