如何使用索引数组来导航对象数组

How can I use an array of indexes to nagivate an array of objects

我正在制作清单应用程序,需要浏览我的数据以编辑 complete:false

我的数据结构中子项是可选键,具有子项的对象不会有完整的键。

我有两种方法可以解决这个问题

  1. 使用 path 的每个部分并使用 items[path[0]] 等导航数组
  2. 或将 path 用作(或替换为)唯一的 id 并搜索该 ID

这两个我都不知道怎么做

const TESTDATA = [
  {
    name: 'foo',
    path: [0],
    open: true,
    children: [
      { name: 'bar', completed: false, path: [0, 0] },
      { name: 'data', completed: false, path: [0, 1] },
    ],
  },
  {
    name: 'foo',
    path: [1],
    open: true,
    children: [
      { name: 'bar', completed: false, path: [1, 0] },
      {
        name: 'data',
        completed: false,
        path: [1, 1],
        children: [{ name: 'baz', completed: false, path: [1, 1, 0] }],
      },
    ],
  },
]

有没有办法在所有子键和 return 项中递归(或不搜索)搜索特定的 path/id 以便对其进行修改?

也许这样的事情可以让你开始:

function gather(name, obj){
 if(name==obj.name) return obj;
 if(obj.children) return obj.children.map(x=>gather(name, x)).flat().filter(Boolean)
}

TESTDATA.map(x=>gather("baz", x)).flat()[0]

1。关注 path

这是更直接的方法,因为我们可以直接去找到想要的元素。因此,这可能会更高效。

这是一种方法:

  1. 浏览所有必要的 .childrens。
  2. path中最后一个索引指定的元素。

1.导航 .childrens:

function findChild(path, data) {
  let array = data;
  for (let i = 0; i < path.length - 1; ++i) {
    const pathIndex = path[i];
    array = array[pathIndex].children;
  }
  // ...
}

2。按最后一个索引取元素:

function findChild(path, data) {
  let array = data;
  for (let i = 0; i < path.length - 1; ++i) {
    const pathIndex = path[i];
    array = array[pathIndex].children;
  }
  return array[path[path.length - 1]];
}

我们实际上还没有考虑 edge-cases,即:

  1. 如果 data(或此处的任何数组)是空数组怎么办?
  2. 如果给定路径上的元素没有 .children 怎么办?

这里有一个解决方法:

function findChild(path, data) {
  let array = data;
  for (let i = 0; i < path.length - 1 && array; ++i) {
    const pathIndex = path[i];
    array = array[pathIndex]?.children;
  }
  return array?.[path[path.length - 1]];
}

function findChild(path, data) {
  let array = data;
  for (let i = 0; i < path.length - 1 && array; ++i) {
    const pathIndex = path[i];
    array = array[pathIndex]?.children;
  }
  return array?.[path[path.length - 1]];
}

const data = [
  {
    name: 'foo',
    path: [0],
    open: true,
    children: [
      { name: 'bar', completed: false, path: [0, 0] },
      { name: 'data', completed: false, path: [0, 1] },
    ],
  },
  {
    name: 'foo',
    path: [1],
    open: true,
    children: [
      { name: 'bar', completed: false, path: [1, 0] },
      {
        name: 'data',
        completed: false,
        path: [1, 1],
        children: [{ name: 'baz', completed: false, path: [1, 1, 0] }],
      },
    ],
  },
];

console.time();
const result = findChild([1, 0], data);
console.timeEnd();
console.log("Imperative:", result);

2。按 path(作为 ID)

查找

理想情况下,我们将所有元素放在一个平面数组中,因为这样我们就可以简单地进行 Array.find() 搜索。

因为我们最初获取的是tree-like结构中的元素,所以我们必须手动将它们展平。这是一种方法:

const _flatten = (el) => ([el, ...(el.children ?? []).flatMap(_flatten)]);
const flatten = (array) => array.flatMap(_flatten);

现在我们有了所有元素的平面数组,我们可以使用 Array.find():

function findChild(path, treeElements) {
  // When equal, serializes to the same string
  const isEqual = (p1, p2) => p1.join() === p2.join();

  const _flatten = (el) => ([el, ...(el.children ?? []).flatMap(_flatten)]);
  const flatten = (array) => array.flatMap(_flatten);

  return flatten(treeElements).find(el => isEqual(el.path, path));
}

function findChild(path, treeElements) {
  // When equal, serializes to the same string
  const isEqual = (p1, p2) => p1.join() === p2.join();

  const _flatten = (el) => ([el, ...(el.children ?? []).flatMap(_flatten)]);
  const flatten = (array) => array.flatMap(_flatten);

  return flatten(treeElements).find(el => isEqual(el.path, path));
}

const data = [
  {
    name: 'foo',
    path: [0],
    open: true,
    children: [
      { name: 'bar', completed: false, path: [0, 0] },
      { name: 'data', completed: false, path: [0, 1] },
    ],
  },
  {
    name: 'foo',
    path: [1],
    open: true,
    children: [
      { name: 'bar', completed: false, path: [1, 0] },
      {
        name: 'data',
        completed: false,
        path: [1, 1],
        children: [{ name: 'baz', completed: false, path: [1, 1, 0] }],
      },
    ],
  },
];

console.time();
const result = findChild([1, 0], data);
console.timeEnd();
console.log("Functional:", result);

1和2的比较

根据 JSBen.ch,与预期相比,第一个选项比第二个选项快大约 4-5 倍。