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

如何在JavaScript中通过动态路径高效实现对象值设置(含数组操作)

实现支持动态路径的嵌套对象值设置函数

需求说明

需要编写一个JavaScript函数setValue,满足以下要求:

  • 支持通过字符串路径或数组路径在嵌套对象/数组中设置值
  • 路径需兼容数组索引(如[2])和数组推入操作(如[])
  • 正确处理类似"foo.bar[][2]"或"foo.bar[2][]"的路径格式

实现思路

  1. 路径统一解析:先将字符串路径转换为标准数组格式,把.分隔的属性、[](推入)、[数字](索引)分别转换为数组元素(空字符串代表推入,数字代表索引)
  2. 逐层遍历创建容器:从根对象开始,逐个处理路径节点,确保每一层的容器类型正确(对象/数组),不存在则根据下一个节点类型自动创建对应容器
  3. 最终节点赋值:处理最后一个路径节点,根据节点类型(属性名、索引、推入)完成值的设置

完整代码实现

// 解析路径:将字符串路径转为标准数组格式
function parsePath(path) {
  if (Array.isArray(path)) return path;
  // 匹配属性名 + 可选的[]部分
  const parts = path.match(/([^\.\[]+)(?:\[([^\]]+)\])?/g) || [];
  return parts.flatMap(part => {
    const matchResult = part.match(/([^\.\[]+)(?:\[([^\]]+)\])?/);
    if (!matchResult) return [];
    const [, key, index] = matchResult;
    const result = [key];
    if (index !== undefined) {
      // 处理[]:空字符串代表推入,非空则转为数字索引
      result.push(index === '' ? '' : Number(index));
    }
    return result;
  });
}

// 核心设置函数
function setValue(obj, path, value) {
  const pathParts = parsePath(path);
  let current = obj;

  // 遍历到倒数第二个节点,准备最后一步赋值
  for (let i = 0; i < pathParts.length - 1; i++) {
    const key = pathParts[i];
    const nextKey = pathParts[i + 1];
    const nextIsIndex = typeof nextKey === 'number';

    if (key === '') {
      // 处理推入操作[]:当前必须是数组,push对应类型的新容器
      if (!Array.isArray(current)) {
        throw new Error(`路径第${i}段预期为数组,实际不是`);
      }
      const newItem = nextIsIndex ? [] : {};
      current.push(newItem);
      current = newItem;
    } else if (typeof key === 'number') {
      // 处理数组索引:确保数组存在,索引位置不足则补空
      if (!Array.isArray(current)) {
        throw new Error(`路径第${i}段预期为数组,实际不是`);
      }
      if (key >= current.length) {
        current.length = key + 1;
      }
      // 索引位置为空时,根据下一个节点类型创建容器
      if (current[key] == null) {
        current[key] = nextIsIndex ? [] : {};
      }
      current = current[key];
    } else {
      // 处理对象属性:确保对象存在,属性为空则创建对应容器
      if (typeof current !== 'object' || current == null) {
        throw new Error(`路径第${i}段预期为对象,实际不是`);
      }
      if (current[key] == null) {
        current[key] = nextIsIndex ? [] : {};
      }
      current = current[key];
    }
  }

  // 处理最后一个节点,完成赋值
  const lastKey = pathParts[pathParts.length - 1];
  if (lastKey === '') {
    // 最后一步是推入操作
    if (!Array.isArray(current)) {
      throw new Error(`最终路径段为[],预期当前为数组`);
    }
    current.push(value);
  } else if (typeof lastKey === 'number') {
    // 最后一步是数组索引赋值
    if (!Array.isArray(current)) {
      throw new Error(`最终路径段为索引${lastKey},预期当前为数组`);
    }
    if (lastKey >= current.length) {
      current.length = lastKey + 1;
    }
    current[lastKey] = value;
  } else {
    // 最后一步是对象属性赋值
    if (typeof current !== 'object' || current == null) {
      throw new Error(`最终路径段为属性${lastKey},预期当前为对象`);
    }
    current[lastKey] = value;
  }
}

测试示例

// 测试路径"foo.bar[][2]"
const obj1 = { foo: { bar: [] } };
setValue(obj1, 'foo.bar[][2]', 'value');
console.log(obj1); 
// 输出:{ foo: { bar: [[ <2 empty items>, 'value' ]] } }

// 测试路径"foo.bar[2][]"
const obj2 = { foo: { bar: [] } };
setValue(obj2, 'foo.bar[2][]', 'value');
console.log(obj2); 
// 输出:{ foo: { bar: [ <2 empty items>, [ 'value' ] ] } }

关键细节说明

  • 路径解析:通过正则表达式拆分字符串路径,将"foo.bar[][2]"转换为["foo", "bar", "", 2],统一后续处理逻辑
  • 容器自动创建:根据下一个路径节点的类型(索引/属性)自动创建数组或对象,避免手动初始化嵌套结构
  • 数组索引补空:当索引超出当前数组长度时,通过设置length属性补全空元素,符合示例中的空项效果
  • 类型校验:每一步都校验当前容器的类型,不符合路径要求时抛出明确错误,便于调试

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 18:26:00