递归过滤对象数组 [英] Recursively filter array of objects

查看:25
本文介绍了递归过滤对象数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

用这个撞墙,我想我会把它贴在这里,以防有好心人遇到类似的.我有一些看起来像这样的数据:

Hitting a wall with this one, thought I would post it here in case some kind soul has come across a similar one. I have some data that looks something like this:

const input = [
  {
    value: 'Miss1',
    children: [
      { value: 'Miss2' },
      { value: 'Hit1', children: [ { value: 'Miss3' } ] }
    ]
  },
  {
    value: 'Miss4',
    children: [
      { value: 'Miss5' },
      { value: 'Miss6', children: [ { value: 'Hit2' } ] }
    ]
  },
  {
    value: 'Miss7',
    children: [
      { value: 'Miss8' },
      { value: 'Miss9', children: [ { value: 'Miss10' } ] }
    ]
  },
  {
    value: 'Hit3',
    children: [
      { value: 'Miss11' },
      { value: 'Miss12', children: [ { value: 'Miss13' } ] }
    ]
  },
  {
    value: 'Miss14',
    children: [
      { value: 'Hit4' },
      { value: 'Miss15', children: [ { value: 'Miss16' } ] }
    ]
  },
];

我在运行时不知道层次结构有多深,即有多少层对象会有一个子数组.我已经稍微简化了这个例子,我实际上需要将 value 属性与一组搜索词进行匹配.让我们暂时假设我匹配 where value.includes('Hit').

I don't know at run time how deep the hierarchy will be, i.e. how many levels of objects will have a children array. I have simplified the example somewhat, I will actually need to match the value properties against an array of search terms. Let's for the moment assume that I am matching where value.includes('Hit').

我需要一个返回新数组的函数,例如:

I need a function that returns a new array, such that:

  • 每个没有子级或子级层次结构中没有匹配项的非匹配对象不应存在于输出对象中

  • Every non-matching object with no children, or no matches in children hierarchy, should not exist in output object

每个具有包含匹配对象的后代的对象都应该保留

Every object with a descendant that contains a matching object, should remain

匹配对象的所有后代都应该保留

All descendants of matching objects should remain

在这种情况下,我正在考虑将匹配对象"作为包含字符串 Hitvalue 属性的对象,反之亦然.

I am considering a 'matching object' to be one with a value property that contains the string Hit in this case, and vice versa.

输出应如下所示:

const expected = [
  {
    value: 'Miss1',
    children: [
      { value: 'Hit1', children: [ { value: 'Miss3' } ] }
    ]
  },
  {
    value: 'Miss4',
    children: [
      { value: 'Miss6', children: [ { value: 'Hit2' } ] }
    ]
  },
  {
    value: 'Hit3',
    children: [
      { value: 'Miss11' },
      { value: 'Miss12', children: [ { value: 'Miss13' } ] }
    ]
  },
  {
    value: 'Miss14',
    children: [
      { value: 'Hit4' },
    ]
  }
];

非常感谢花时间阅读本文的任何人,如果我先到达那里,将发布我的解决方案.

Many thanks to anyone who took the time to read this far, will post my solution if I get there first.

推荐答案

使用 .filter() 并按照我在上面的评论中描述的递归调用基本上是您所需要的.您只需要在返回之前使用递归调用的结果更新每个 .children 属性.

Using .filter() and making a recursive call as I described in the comment above is basically what you need. You just need to update each .children property with the result of the recursive call before returning.

返回值只是结果 .children 集合的 .length,因此如果至少有一个,则保留该对象.

The return value is just the .length of the resulting .children collection, so if there's at least one, the object is kept.

var res = input.filter(function f(o) {
  if (o.value.includes("Hit")) return true

  if (o.children) {
    return (o.children = o.children.filter(f)).length
  }
})

const input = [
  {
    value: 'Miss1',
    children: [
      { value: 'Miss2' },
      { value: 'Hit1', children: [ { value: 'Miss3' } ] }
    ]
  },
  {
    value: 'Miss4',
    children: [
      { value: 'Miss5' },
      { value: 'Miss6', children: [ { value: 'Hit2' } ] }
    ]
  },
  {
    value: 'Miss7',
    children: [
      { value: 'Miss8' },
      { value: 'Miss9', children: [ { value: 'Miss10' } ] }
    ]
  },
  {
    value: 'Hit3',
    children: [
      { value: 'Miss11' },
      { value: 'Miss12', children: [ { value: 'Miss13' } ] }
    ]
  },
  {
    value: 'Miss14',
    children: [
      { value: 'Hit4' },
      { value: 'Miss15', children: [ { value: 'Miss16' } ] }
    ]
  },
];

var res = input.filter(function f(o) {
  if (o.value.includes("Hit")) return true

  if (o.children) {
    return (o.children = o.children.filter(f)).length
  }
})
console.log(JSON.stringify(res, null, 2))

请注意,String 上的 .includes() 是 ES7,因此可能需要针对旧版浏览器进行修补.您可以使用传统的 .indexOf("Hit") != -1 代替它.

Note that .includes() on a String is ES7, so may need to be patched for legacy browsers. You can use the traditional .indexOf("Hit") != -1 in its place.

为了不改变原始对象,创建一个映射函数来复制一个对象并在过滤器之前使用它.

To not mutate the original, create a map function that copies an object and use that before the filter.

function copy(o) {
  return Object.assign({}, o)
}

var res = input.map(copy).filter(function f(o) {
  if (o.value.includes("Hit")) return true

  if (o.children) {
    return (o.children = o.children.map(copy).filter(f)).length
  }
})

<小时>

要真正压缩代码,您可以这样做:


To really squeeze the code down, you could do this:

var res = input.filter(function f(o) {
  return o.value.includes("Hit") ||
         o.children && (o.children = o.children.filter(f)).length
})

虽然读起来有点困难.

这篇关于递归过滤对象数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆