You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将带嵌套扁平键的JavaScript对象转为嵌套JSON对象

问题:将带斜杠分隔嵌套键的扁平JavaScript对象解析为嵌套JSON对象

需求说明

需要把使用斜杠(/)分隔嵌套层级的扁平对象,转换为包含多层数组和对象的嵌套结构,同时要支持非连续数组索引(比如0、1、10这类不连续的索引值),实现通用的递归逻辑适配各种嵌套场景。

待解析的扁平对象

const object = {
  "item1": "value",
  "item2/0/subitem1": "value",
  "item2/0/subitem2": "value",
  "item2/1/subitem1": "value",
  "item2/1/subitem2": "value",
  "item3/0/subitem1/subsubitem1": "value",
  "item3/1/subitem1/subsubitem1": "value",
  "item4/0/subitem1/0/subsubitem1": "value",
  "item4/0/subitem1/1/subsubitem1": "value",
}

期望转换后的嵌套对象

const parsedObject = {
  item1: "value",
  item2: [
    {
      subitem1: "value",
      subitem2: "value",
    },
    {
      subitem1: "value",
      subitem2: "value",
    }
  ],
  item3: [
    {
      subitem1: {
        subsubitem1: "value",
      }
    },
    {
      subitem1: {
        subsubitem1: "value",
      }
    },
  ],
  item4: [
    {
      subitem1: [
        {
          subsubitem1: "value",
        },
        {
          subsubitem1: "value",
        },
      ]
    }
  ]
}

当前瓶颈与尝试的代码

已编写处理函数,但无法支持非连续数组索引,需要优化递归逻辑以适配多层嵌套数组与对象的通用场景。尝试的代码如下:

const object = {
  "item1": "value",
  "item2/0/subitem1": "value",
  "item2/0/subitem2": "value",
  "item2/1/subitem1": "value",
  "item2/1/subitem2": "value",
  "item3/0/subitem1/subsubitem1": "value",
  "item3/1/subitem1/subsubitem1": "value",
  "item4/0/subitem1/0/subsubitem1": "value",
  "item4/0/subitem1/1/subsubitem1": "value",
}

const mainFunction = () => {
  const specie = {};
  const map = new Map();
  Object.keys(object).forEach((key) => {
    const keyTokens = key.split('/');
    processKeyValues(specie, keyTokens, object[key], map);
  });
  console.log(specie);
}

const convertStringToBoolean = (str) => {
  return str.toLowerCase() === 'true' ? true : str.toLowerCase() === 'false' ? false : str;
};

const processKeyValues = (specie, keyTokens, value, map) => {
  keyTokens.reduce((prev, curr, index, array) => {
    if (index === array.length - 1) {
      const valuePossibleNumber = parseInt(value); //transform number strings into numbers
      if (!isNaN(valuePossibleNumber)) {
        prev[curr] = valuePossibleNumber;
        return;
      }
      prev[curr] = convertStringToBoolean(value); //boolean strings into booleans
      return;
    }
    const currentKeyPossibleNumber = parseInt(curr);
    const currentKeyIsNumber = !isNaN(currentKeyPossibleNumber);
    const nextKeyPossibleNumber = parseInt(array[index + 1]);
    const nextKeyIsNumber = !isNaN(nextKeyPossibleNumber);

    if (currentKeyIsNumber) {
      const hasMappedIndex = map.has(currentKeyPossibleNumber);
      let mappedIndex = map.get(currentKeyPossibleNumber);
      if (!hasMappedIndex) {
        mappedIndex = map.size;
        map.set(currentKeyPossibleNumber, mappedIndex);
      }
      const currentKeyAsIndexDoesNotExist = !prev[mappedIndex];
      if (currentKeyAsIndexDoesNotExist) {
        if (nextKeyIsNumber) {
          prev.push([]);
        } else {
          prev.push({});
        }
      }
      return prev[mappedIndex];
    }
    const currentKeyDoesNotExist = !prev[curr];
    if (currentKeyDoesNotExist) {
      if (nextKeyIsNumber) {
        prev[curr] = [];
        map.clear();
      } else {
        prev[curr] = {};
      }
    }
    return prev[curr];
  }, specie);
};

解决方案

核心思路

  1. 遍历每个扁平键,按斜杠分割为层级路径数组
  2. 迭代处理路径,判断每个路径段是否为数组索引(纯数字字符串)
  3. 对于数组,直接使用原始索引值,不存在的索引位置保留默认状态(可按需调整补空逻辑)
  4. 根据下一个路径段的类型,自动初始化当前层级为数组或对象

优化后的代码

const flattenToNested = (flatObj) => {
  const result = {};

  // 处理值的类型转换(数字、布尔值)
  const parseValue = (val) => {
    if (!isNaN(val) && !isNaN(parseFloat(val))) {
      return Number(val);
    }
    if (val.toLowerCase() === 'true') return true;
    if (val.toLowerCase() === 'false') return false;
    return val;
  };

  // 处理单个键值对的嵌套逻辑
  const processEntry = (key, value) => {
    const pathSegments = key.split('/');
    let current = result;

    for (let i = 0; i < pathSegments.length; i++) {
      const segment = pathSegments[i];
      const isLastSegment = i === pathSegments.length - 1;
      // 判断当前段是否为数组索引(纯数字格式)
      const isIndex = /^\d+$/.test(segment);
      const index = Number(segment);

      if (isLastSegment) {
        // 最后一段,直接赋值
        if (isIndex) {
          if (!Array.isArray(current)) {
            throw new Error(`路径${pathSegments.slice(0, i).join('/')}不是数组,无法赋值索引`);
          }
          current[index] = parseValue(value);
        } else {
          current[segment] = parseValue(value);
        }
        return;
      }

      // 非最后一段,构建嵌套结构
      if (isIndex) {
        if (!Array.isArray(current)) {
          throw new Error(`路径${pathSegments.slice(0, i).join('/')}不是数组,无法创建索引`);
        }
        // 索引位置不存在时,根据下一段类型初始化结构
        if (!current[index]) {
          const nextIsIndex = /^\d+$/.test(pathSegments[i+1]);
          current[index] = nextIsIndex ? [] : {};
        }
        current = current[index];
      } else {
        // 对象键不存在时,根据下一段类型初始化结构
        if (!current[segment]) {
          const nextIsIndex = /^\d+$/.test(pathSegments[i+1]);
          current[segment] = nextIsIndex ? [] : {};
        }
        current = current[segment];
      }
    }
  };

  // 遍历所有扁平键值对处理
  Object.entries(flatObj).forEach(([key, value]) => {
    processEntry(key, value);
  });

  return result;
};

// 测试示例(包含非连续索引场景)
const object = {
  "item1": "value",
  "item2/0/subitem1": "value",
  "item2/0/subitem2": "value",
  "item2/1/subitem1": "value",
  "item2/1/subitem2": "value",
  "item3/0/subitem1/subsubitem1": "value",
  "item3/1/subitem1/subsubitem1": "value",
  "item4/0/subitem1/0/subsubitem1": "value",
  "item4/0/subitem1/1/subsubitem1": "value",
  "item5/2/subitem": "non-consecutive",
  "item5/0/subitem": "first"
};

console.log(JSON.stringify(flattenToNested(object), null, 2));

代码说明

  • 类型转换:保留原逻辑,自动将数字字符串、布尔值字符串转为对应类型
  • 非连续索引支持:直接使用原始数字索引,数组中未赋值的索引位置会保留为undefined,可根据需求添加补空逻辑
  • 结构自动判断:根据下一个路径段是否为数字,自动初始化当前层级为数组或对象
  • 错误处理:添加基础的类型校验,避免非法结构赋值

内容的提问来源于stack exchange,提问作者Florin200217

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 23:10:27