Skip to content

递归树状数据,查找祖先节点并数组输出

Published: at 06:12 AMSuggest Changes

需求

处理以下树状数据,优雅的找到自己的祖先并用数组输出

let defaultDataa = [
  {
    name: '活动名称一',
  },
  {
    name: '活动名称二',
    children: [
      {
        name: '活动名称三',
        children: [
          {
            name: '活动名称四',
            children: [
              {
                name: '活动名称五',
              },
            ],
          },
        ],
      },
    ],
  },
];

find(defaultDataa, '活动名称四')[
  // out
  ('活动名称二', '活动名称三', '活动名称四')
];

解决方法

这是我自己的解决方法,很普通的递归,真的很平凡。。

function find(array, text) {
  for (let index = 0; index < array.length; index++) {
    const element = array[index];
    let { name, children } = element;
    if (name == text) {
      return [name];
    } else {
      if (children) {
        let getChildrenArr = find(children, text);
        if (getChildrenArr.length > 0) {
          return [name, ...getChildrenArr];
        }
      }
    }
  }
  return [];
}

console.log(find(testData, '活动名称四'));

德国巨佬 可爱的 kiochan 的方法,他还发现了我的一个 BUG,见后记,不看后悔

function find(arr, name) {
  for (const o of arr) {
    if (o && o.name) {
      if (o.name === name) {
        return [o.name];
      } else if (o.children instanceof Array) {
        const next = find(o.children, name);
        if (next && next.length > 0) {
          return [o.name, ...find(o.children, name)];
        }
      }
    }
  }
  return null;
}

资深前端醒醒的方法,这代码太强了,看的我一脸茫然,让我意识到差距。注意一下代码中的注释

// 考虑的很全面,注意那个 key 和 children 可以自定义想找的数据类型。
const findDeep = (arr, val, { key = 'id', children = 'children' } = {}) => {
  let list;
  function find(v) {
    if (v[key] === val) {
      return this.push(v[key]) && (list = this);
    }

    if (v?.[children]?.length) {
      return this.push(v[key]) && v[children].find(find, [...this]);
    }
  }

  // 问了一下是不是可以用 map,他说这个方法找到了就不再进行递归了,速度快,我当时就觉得 NB
  arr.find((v) => find.bind([])(v, val));

  return list;
};

Max 大佬的方法,没有用到递归,这段代码值得一看 我得好好研究一下。。一下子没看懂。

来自大佬的解释: 基本逻辑就是 往 iter 里面塞对象,每个对象记录他的 path,虽然效率堪忧 基本上把整个数据集复制了一遍 不过下次使用的时候可以复用 可以把 iter 提出来做缓存,不过这个前提建立在数据不变 可以这么玩,不然的话 就是《gc 过着很难的日子》

function getPath(data, name) {
  let iter = data.map((datum) => ({ ...datum, path: [] }));
  for (let pos = 0; pos < iter.length; pos++) {
    const item = iter[pos];
    if (item.name === name) return [...item.path, name];
    if (item.children)
      iter = [
        ...iter,
        ...item.children.map((child) => ({
          ...child,
          path: [...item.path, item.name],
        })),
      ];
  }
  return false;
}

群友 成都 - 入门到放弃 - 嘻哈 的代码

function find(tree, field, val) {
  let newArr = [];
  tree.forEach((item) => {
    let arr = [];
    JSON.stringify(item, (a, b) => {
      if (b[field]) arr.push(b[field]);
      return b;
    });
    arr.reverse();
    newArr = arr.slice(arr.findIndex((e) => e == val));
  });
  return newArr;
}

后记

上面的方法都是不断优化而来的,在写的过程中还发现了有意思的 BUG

我最初的代码是这样的,乍看上去没啥问题

function find(array, text) {
  for (let index = 0; index < array.length; index++) {
    const element = array[index];
    let { name, children } = element;
    if (name == text) {
      return [name];
    } else {
      if (children) {
        let getChildrenArr = find(children, text);
        return [name, ...getChildrenArr];
      }
    }
  }
  return [];
}

严谨的德国巨佬 可爱的 kiochan 给出了这样的一个数据样本,注意活动名称四下面有一个空数组

const testData = [
  {
    name: '活动名称一',
  },
  {
    name: '活动名称二',
    children: [
      {
        name: '活动名称三',
        children: [
          {
            name: '活动名称四',
            children: [],
          },
        ],
      },
    ],
  },
  {
    name: '活动名称二',
    children: [
      {
        name: '活动名称三',
        children: [
          {
            name: '活动名称四',
            children: [
              {
                name: '活动名称五',
              },
            ],
          },
        ],
      },
    ],
  },
];

由于我没有做校验,递归到空数组那一块就停止了,输出了错误的结果。

然后我加上下面这段代码解决了问题

if (getChildrenArr.length > 0) {
  return [name, ...getChildrenArr];
}

Previous Post
React useState 更新数组无效的问题及解决方法
Next Post
强大的 JSON.stringify 方法