简体   繁体   English

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

[英]How can I use an array of indexes to nagivate an array of objects

I'm making a checklist app and need to navigate through my data to edit the complete:false我正在制作一个清单应用程序,需要浏览我的数据以编辑complete:false

My data is structured with children being an optional key, and objects with children won't have the completed key.我的数据结构中,children 是一个可选的键,而有children 的对象没有完整的键。

There are two ways I could solve this有两种方法可以解决这个问题

  1. use each part of the path and navigate the array using items[path[0]] and so on使用path的每个部分并使用items[path[0]]等导航数组
  2. Or use path as (or replace it with) a unique id and search for that id或使用path作为(或替换为)唯一id并搜索该ID

both of which I do not know how to do这两个我都不知道该怎么做

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] }],
      },
    ],
  },
]

Is there a way to recursively (or not) search for a specific path/id in all the children keys and return the item so it could be modified?有没有办法递归(或不)在所有子键中搜索特定路径/ID并返回该项目以便对其进行修改?

maybe something like this can get you started:也许这样的事情可以让你开始:

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. Follow path 1.跟随path

This is the more direct way, as we can literally go and find the wanted element.这是更直接的方法,因为我们可以从字面上找到想要的元素。 As such, this is likely more performant.因此,这可能更高效。

Here is one way to do it:这是一种方法:

  1. Navigate through all necessary .children .浏览所有必要的.children
  2. Take the element specified by the last index in path .path中最后一个索引指定的元素。

1. Navigate .children : 1. 导航.children

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. Take element by last index: 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]];
}

We actually haven't yet taken edge-cases into account, namely:我们实际上还没有考虑边缘情况,即:

  1. What if data (or any array here) is an empty array?如果data (或此处的任何数组)是一个空数组怎么办?
  2. What if elements along the given path don't have .children ?如果给定路径上的元素没有.children怎么办?

Here's a fix to that:这是一个修复:

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. Find by path (as ID) 2.按path查找(作为ID)

Ideally we have all elements in a single, flat array, because then we can simply do a Array.find() search.理想情况下,我们将所有元素放在一个单一的平面数组中,因为这样我们就可以简单地进行Array.find()搜索。

Because we initially get the elements in a tree-like structure, we have to flatten them manually.因为我们最初以树状结构获取元素,所以我们必须手动将它们展平。 Here is one way to do that:这是一种方法:

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

Now that we have a flat array of all the elements, we can use Array.find() :现在我们有了一个包含所有元素的平面数组,我们可以使用Array.find()

function findChild(path, treeElements) {
  const isEqual = (a1, a2) => {
    if (a1.length !== a2.length) return false;
    return a1.every((v, i) => v === a2[i]);
  };

  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) { const isEqual = (a1, a2) => { if (a1.length !== a2.length) return false; return a1.every((v, i) => v === a2[i]); }; 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);

Comparison between 1. and 2. 1. 和 2 之间的比较。

As per JSBen.ch , the first option compared to the second option is about 3-4 times faster, as expected.根据JSBen.ch ,与第二个选项相比,第一个选项快了大约 3-4 倍,正如预期的那样。

(The implementation for the isEqual() function is the quickest as per this benchmark .) (根据这个基准isEqual()函数的实现是最快的。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM