JavaScript 层级数组常用工具方法(一)—— 数组结构转换

概述

本文介绍了 JavaScript 中层级数组的常用工具方法之一,即数组结构转换。在处理层级数组时,经常需要将数组从一种结构转换为另一种结构,以满足不同的需求。本文将探讨如何使用 JavaScript 中的一些常用方法来实现数组结构转换,包括数组的扁平化、数组的嵌套结构转换。通过学习这些常用的数组结构转换技巧,可以更加高效地处理层级数组数据,提升 JavaScript 编程的效率与灵活性。

普通数组转层级数组

普通数组转层级数组是一种将扁平化的数组数据转换为层级结构的操作。层级数组通常用于表示树状数据结构,其中每个对象都包含了节点的信息以及其父节点的信息。

// 测试数据
const flatArray = [
  { id: 1, name: "Node 1", parentId: null },
  { id: 2, name: "Node 2", parentId: 1 },
  { id: 3, name: "Node 3", parentId: 1 },
  { id: 4, name: "Node 4", parentId: 2 },
  { id: 5, name: "Node 5", parentId: 2 },
  { id: 6, name: "Node 6", parentId: 3 },
  { id: 7, name: "Node 7", parentId: 3 },
];

1.Map 数据结构

利用 Map 数据结构来存储每个节点,并根据父级 ID 进行索引,然后遍历普通数组,根据每个节点的父级 ID 在 Map 中找到对应的父节点,并将当前节点添加到父节点的 children 数组中,从而实现普通数组到层级数组的转换。

/**
 * Map 数据结构
 * @param arr 普通数据
 */
function convertToHierarchyWithMap(arr) {
  const result = [];
  const map = new Map();

  for (const item of arr) {
    map.set(item.id, { ...item, children: [] });
  }

  for (const item of arr) {
    if (item.parentId !== null && map.has(item.parentId)) {
      map.get(item.parentId).children.push(map.get(item.id));
    } else {
      result.push(map.get(item.id));
    }
  }
  return result;
}

// 使用示例
const hierarchyData = convertToHierarchyWithMap(flatArray);
console.log(hierarchyData);

2.Set 数据结构

利用 Set 数据结构来存储每个节点的 ID,然后遍历普通数组,根据每个节点的父级 ID 在 Set 中查找对应的父节点,将当前节点添加到父节点的 children 数组中,从而实现普通数组到层级数组的转换。

/**
 * Set 数据结构
 * @param arr 普通数据
 */
function convertToHierarchyWithSet(arr) {
  const result = [];
  const idSet = new Set();

  for (const item of arr) {
    idSet.add(item.id);
  }

  for (const item of arr) {
    if (item.parentId !== null && idSet.has(item.parentId)) {
      const parent = arr.find(node => node.id === item.parentId);
      if (!parent.children) {
        parent.children = [];
      }
      parent.children.push(item);
    } else {
      result.push(item);
    }
  }

  return result;
}

// 使用示例
const hierarchyData = convertToHierarchyWithSet(flatArray);
console.log(hierarchyData);

3.递归方式

使用递归算法遍历普通数组,根据每个节点的父级 ID 将其添加到对应的父节点下的 children 数组中,从而实现普通数组到层级数组的转换。递归方式较为简洁,但在处理大型普通数组时可能存在性能上的限制。

/**
 * 递归方式
 * @param arr 普通数据
 * @param parentId 父级节点ID
 */
function convertToHierarchyRecursively(arr, parentId = null) {
  const result = [];
  for (const item of arr) {
    if (item.parentId === parentId) {
      const newItem = { ...item, children: convertToHierarchyRecursively(arr, item.id) };
      result.push(newItem);
    }
  }
  return result;
}

// 使用示例
const hierarchyData = convertToHierarchyRecursively(flatArray);
console.log(hierarchyData);

4.迭代方式

使用循环和对象字典等数据结构,遍历普通数组,将每个节点根据其父级 ID 添加到对应的父节点下的 children 数组中,从而实现普通数组到层级数组的转换。迭代方式相对于递归方式,可能在性能上具有一定的优势,尤其是在处理大型普通数组时。

/**
 * 迭代方式
 * @param arr 普通数据
 */
function convertToHierarchyIteratively(arr) {
  const result = [];
  const dict = {};

  for (const item of arr) {
    dict[item.id] = { ...item, children: [] };
  }

  for (const item of arr) {
    if (item.parentId !== null && dict[item.parentId]) {
      dict[item.parentId].children.push(dict[item.id]);
    } else {
      result.push(dict[item.id]);
    }
  }

  return result;
}

// 使用示例
const hierarchyData = convertToHierarchyIteratively(flatArray);
console.log(hierarchyData);

5.reduce 函数方式

利用 reduce 函数将普通数组转换为层级数组。reduce 函数可以用来迭代数组并在每一步中更新累积值,从而实现对普通数组到层级数组的转换。

/**
 * reduce 函数方式
 * @param arr 普通数据
 */
function convertToHierarchyWithReduce(arr) {
  return arr.reduce((result, item) => {
    const newItem = { ...item, children: [] };
    const parent = result.find(parentItem => parentItem.id === item.parentId);
    if (parent) {
      parent.children.push(newItem);
    } else {
      result.push(newItem);
    }
    return result;
  }, []);
}

// 使用示例
const hierarchyData = convertToHierarchyWithReduce(flatArray);
console.log(hierarchyData);

6.Generator 方式

这种方式利用 Generator 函数的特性,能够在遍历扁平化数组时递归地生成层级数组中的节点对象,并且具有较好的可读性和扩展性。

/**
 * Generator 方式
 * @param arr 普通数据
 */
function* convertToHierarchy(flatArray, parentId = null) {
  for (const node of flatArray) {
    if (node.parentId === parentId) {
      yield {
        ...node,
        children: [...convertToHierarchy(flatArray, node.id)]
      };
    }
  }
}
// 使用示例
const hierarchyArray = [...convertToHierarchy(flatArray)];
console.log(hierarchyArray);

推荐总结

推荐的方式取决于你的具体需求和代码环境。以下是一些常见的情况和对应的推荐方式:

  1. 若数据量较小且对性能要求不高,可以使用简单的循环遍历方式或者 reduce 函数来实现层级数组的转换,代码简洁易懂。
  2. 若数据量较大或者对性能有较高要求,可以使用迭代或者 Set 数据结构来实现,这些方法在处理大数据量时可能会有更好的性能表现。
  3. 若需要更加灵活和复杂的数据处理,可以使用递归方式来实现,递归可以处理较为复杂的层级关系,但可能在性能上略有损耗。
  4. 使用 Generator 方式可以在处理大数据量时节省内存,但需要注意兼容性和语法的限制,适合特定场景下使用。

综上所述,推荐的方式取决于具体需求和代码环境,应根据实际情况选择最合适的方法。在实际使用中,可以综合考虑性能、代码复杂性、兼容性等因素,选择最适合自己项目的方式进行普通数组到层级数组的转换。

层级数组转普通数组

层级数组是一种包含多层嵌套关系的数组数据结构,其中每个元素都可以包含子元素(子数组),形成树状结构。而将层级数组转换为普通数组则是将这种树状结构展平成一维数组的过程。

// 测试数据
const data = [
  {
    id: 1,
    name: "Node 1",
    children: [
      { id: 2, name: "Node 1-1", children: [
          { id: 4, name: "Node 1-1-1", children: [] },
          { id: 5, name: "Node 1-1-2", children: [] }
      ] },
      { id: 3, name: "Node 1-2", children: [] }
    ]
  }
];

1.递归扁平化

递归是一种通过函数自身调用的方式来处理数组或其他数据结构的方法,从而实现对多层嵌套结构的遍历和展开。在递归中,函数通过不断调用自身,从而逐层深入层级数组的嵌套结构,直到达到最底层的元素,并将其逐级展开到一维数组中。递归的方式通常较为简洁,但在处理大型层级数组时可能会面临性能和栈溢出的风险。因此,在使用递归方式进行扁平化时需要小心处理递归的终止条件和递归深度,以确保程序的正确性和性能。

/**
 * 递归扁平化
 * @param arr 层级数据
 */
function flattenHierarchy(arr) {
  let result = [];
  arr.forEach(node => {
    result.push(node);
    if (Array.isArray(node.children)) {
      result.push(...flattenArray(node.children));
    }
  });
  return result;
}

// 使用示例
const flattenedData = flattenHierarchy(data);
console.log(flattenedData);

2. 迭代扁平化

迭代是一种通过循环和遍历的方式逐级处理数组或其他数据结构的方法,而不是使用递归的方式。在这种方法中,我们通常使用循环或者栈来遍历层级数组的每个元素,并将其展开到一维数组中。这种方式通常比递归更加直接和效率较高,因为避免了递归调用的开销和潜在的栈溢出风险。

/**
 * 迭代扁平化
 * @param arr 层级数据
 */
function flattenHierarchy(arr) {
  const result = [];
  const stack = [...arr];
  while (stack.length > 0) {
    const item = stack.pop();
    result.push(item);
    if (item.children && item.children.length > 0) {
      stack.push(...item.children);
    }
  }
  return result;
}

// 使用示例
const flattenedData = flattenHierarchy(data);
console.log(flattenedData);

3.reduce扁平化

reduce 是 JavaScript 数组的高阶函数之一,它可以用来对数组中的元素进行累积计算,并返回一个最终结果。通过传入一个累积器函数作为 reduce 函数的参数,可以在累积计算的过程中对数组的每个元素进行处理,从而实现对层级数组的展开。在 reduce 函数的累积器函数中,可以通过合并数组或者嵌套调用 reduce 函数来逐层展开层级数组的嵌套结构,从而将其扁平化为一维数组。使用 reduce 函数的方式通常较为灵活,可以方便地进行自定义处理,并且在处理大型层级数组时性能通常较好。然而,使用 reduce 函数进行扁平化时,同样需要小心处理累积器函数的逻辑,以确保程序的正确性和性能。

/**
 * reduce扁平化
 * @param arr 层级数据
 */
function flattenHierarchy(arr) {
  return arr.reduce((result, item) => {
    const children = item.children ? flattenHierarchy(item.children) : [];
    return result.concat([{ ...item, children }]);
  }, []);
}

// 使用示例
const flattenedData = flattenHierarchy(data);
console.log(flattenedData);

4.Generator扁平化

Generator 是 JavaScript 中的一种特殊类型的函数,它可以通过使用 yield 关键字来实现生成器对象,从而支持在函数执行过程中暂停和继续执行的特性。通过编写一个 Generator 函数,可以在遍历层级数组的过程中使用 yield 来生成新的值,并在需要时暂停执行,从而实现对层级数组的展开。通过递归或者迭代调用 Generator 函数,可以在每次执行时生成扁平化的数组元素,并在下一次执行时继续从暂停的位置继续执行,直到整个层级数组被展开为一维数组。Generator 函数提供了一种比较灵活的方式来处理层级数组的扁平化,并且相较于递归或者迭代方式,可以更加直观地控制生成器函数的执行流程和状态,从而实现对层级数组的自定义处理。需要注意的是,Generator 函数在使用时需要注意其迭代器的状态和执行流程,确保程序的正确性和性能。

/**
 * Generator扁平化
 * @param arr 层级数据
 */
function* flattenHierarchy(arr) {
  for (const item of arr) {
    yield item;
    if (item.children && item.children.length > 0) {
      yield* flattenHierarchy(item.children);
    }
  }
}

// 使用示例
const flattenedData = [...flattenHierarchy(data)];
console.log(flattenedData);

推荐总结

推荐的方法取决于你的具体需求和项目情况。不同的方法有各自的优缺点,可以根据以下因素来选择合适的方法:

  1. 性能要求:如果性能是关键考虑因素,可能需要比较不同方法的性能,并选择效率较高的方法。一些方法可能在处理大型层级数组时性能更好,例如使用迭代、循环等方式。
  2. 兼容性:如果你的项目需要考虑旧版 JavaScript 环境或特定浏览器的兼容性,需要选择那些在目标环境下支持较好的方法。一些新的语法或特性可能在较老的 JavaScript 环境中不被完全支持,需要使用 Polyfill 或转译工具进行兼容处理。
  3. 代码简洁性:一些方法可能更加简洁和易读,能够提高代码的可维护性和可读性。例如,使用递归方式可能更加简洁,但需要注意可能存在堆栈溢出的风险。
  4. 项目需求:不同的项目可能有不同的需求,例如对代码大小的要求、对性能的要求、对兼容性的要求等。根据项目的实际需求来选择合适的方法。

综上所述,推荐的方法应该综合考虑性能、兼容性、代码简洁性以及项目需求等因素,并根据具体情况进行选择。

-结束

更多文章

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

顺芯技术猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值