使用ES6在深度嵌套的javascript对象数组中查找值

14
在一个对象数组中,我需要查找一个value——其中keyactivity:然而activitykey可能会被深层嵌套在数组中,就像这样:
const activityItems = [
    {
        name: 'Sunday',
        items: [
            {
                name: 'Gym',
                activity: 'weights',
            },
        ],
    },
    {
        name: 'Monday',
        items: [
            {
                name: 'Track',
                activity: 'race',
            },
            {
                name: 'Work',
                activity: 'meeting',
            },
            {
                name: 'Swim',
                items: [
                    {
                        name: 'Beach',
                        activity: 'scuba diving',
                    },
                    {
                        name: 'Pool',
                        activity: 'back stroke',
                    },
                ],
            },
        ],    
    },
    {} ...
    {} ...
];

所以我编写了一个递归算法,用于查找数组中是否存在某个活动:

let match = false;
const findMatchRecursion = (activity, activityItems) => {
    for (let i = 0; i < activityItems.length; i += 1) {
        if (activityItems[i].activity === activity) {
            match = true;
            break;
        }

        if (activityItems[i].items) {
            findMatchRecursion(activity, activityItems[i].items);
        }
    }

    return match;
};

有没有一种ES6的方法可以确定这样的数组中是否存在一个活动?

我尝试了这样的方法:

const findMatch(activity, activityItems) {
    let obj = activityItems.find(o => o.items.activity === activity);
    return obj;
}

但是对于深度嵌套的活动,这种方法不起作用。

谢谢

4个回答

10
你可以使用some()方法和递归来查找是否存在任何级别的活动,并返回true/false作为结果。

const activityItems = [{"name":"Sunday","items":[{"name":"Gym","activity":"weights"}]},{"name":"Monday","items":[{"name":"Track","activity":"race"},{"name":"Work","activity":"meeting"},{"name":"Swim","items":[{"name":"Beach","activity":"scuba diving"},{"name":"Pool","activity":"back stroke"}]}]}]

let findDeep = function(data, activity) {
  return data.some(function(e) {
    if(e.activity == activity) return true;
    else if(e.items) return findDeep(e.items, activity)
  })
}

console.log(findDeep(activityItems, 'scuba diving'))


4

虽然不如递归算法优雅,但您可以将数组JSON.stringify()化,得到以下结果:

[{"name":"Sunday","items":[{"name":"Gym","activity":"weights"}]},{"name":"Monday","items":[{"name":"Track","activity":"race"},{"name":"Work","activity":"meeting"},{"name":"Swim","items":[{"name":"Beach","activity":"scuba diving"},{"name":"Pool","activity":"back stroke"}]}]}]

你可以使用模板字面量来搜索该模式:
`"activity":"${activity}"`

完成函数:
findMatch = (activity, activityItems) =>
  JSON.stringify(activityItems).includes(`"activity":"${activity}"`);

const activityItems = [{
    name: 'Sunday',
    items: [{
      name: 'Gym',
      activity: 'weights',
    }, ],
  },
  {
    name: 'Monday',
    items: [{
        name: 'Track',
        activity: 'race',
      },
      {
        name: 'Work',
        activity: 'meeting',
      },
      {
        name: 'Swim',
        items: [{
            name: 'Beach',
            activity: 'scuba diving',
          },
          {
            name: 'Pool',
            activity: 'back stroke',
          },
        ],
      },
    ],
  }
];

findMatch = (activity, activityItems) =>
  JSON.stringify(activityItems).includes(`"activity":"${activity}"`);

console.log(findMatch('scuba diving', activityItems)); //true
console.log(findMatch('dumpster diving', activityItems)); //false


1
首先,你的函数可以通过在递归调用中找到匹配后停止来改进。另外,你同时在外部声明了match并返回它。最好只返回它。
const findMatchRecursion = (activity, activityItems) => {
    for (let i = 0; i < activityItems.length; i += 1) {
        if (activityItems[i].activity === activity) {
            return true;
        }

        if (activityItems[i].items && findMatchRecursion(activity, activityItems[i].items) {
            return true;
        }
    }

    return false;
};

没有内置的深度搜索功能,但是如果您愿意,可以使用具有命名函数的.find

var result = !!activityItems.find(function fn(item) {
  return item.activity === "Gym" || (item.items && item.items.find(fn));
});

2
除非我漏掉了什么,否则这将返回包含搜索对象的最顶层父级,而不是对象本身。 - slanden

1
我们现在使用object-scan来处理简单的数据任务,这个工具非常好用,一旦您掌握了如何使用它。下面是如何回答您的问题。

// const objectScan = require('object-scan');

const find = (activity, input) => objectScan(['**'], {
  abort: true,
  rtn: 'value',
  filterFn: ({ value }) => value.activity === activity
})(input);

const activityItems = [{"name":"Sunday","items":[{"name":"Gym","activity":"weights"}]},{"name":"Monday","items":[{"name":"Track","activity":"race"},{"name":"Work","activity":"meeting"},{"name":"Swim","items":[{"name":"Beach","activity":"scuba diving"},{"name":"Pool","activity":"back stroke"}]}]}]

console.log(find('scuba diving', activityItems));
// => { name: 'Beach', activity: 'scuba diving' }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.8.0"></script>

免责声明:我是object-scan的作者。


我正在尝试使用这个库,但是出现了以下错误。请问您能否帮忙检查一下?"<a class='gotoLine' href='#45:33'>45:33</a> Uncaught TypeError: Cannot read properties of null (reading 'escalationId')"以下是代码和数据JSON的JSFiddle链接:https://jsfiddle.net/ksgsharma/59hkdmjb/ - Sharma K
1
@SharmaK 当然,给你:http://jsfiddle.net/jLg3kpd6/ - vincent
跟进问题,从附加的jsfiddle数据中想要找到所有具有“referenceField:true”的元素。 - Sharma K
本质上,因为“referenceField”位于“properties”下属于我要查找的实际元素,所以它类似于祖父节点。 - Sharma K
1
在stackoverflow上创建了一个新问题。谢谢。 - Sharma K
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接