【问题标题】:How to write recursion where items depend on each other?如何编写项目相互依赖的递归?
【发布时间】:2020-06-10 06:23:08
【问题描述】:

我想写一个函数,它返回一个人所依赖的人的列表。但是,人所依赖的,也可以反过来依赖人自己。例如:

const people = {
    'james': {
        reliesOn: [
            'gemma',
            'jessica'
        ]
    },
    jessica: {
        reliesOn: [
            'gemma',
            'peter'
        ]
    },
    peter: {
        reliesOn: [
            'james',
            'gemma',
            'jessica',
            'ivon',
            'jamie'
        ]
    },
    jamie: {
        reliesOn: [
            'ivon'
        ]
    }
}

我正在尝试以最简单的代码获得以下结果

詹姆斯依赖于: gemma, jessica, peter, ivon, jamie

杰西卡依赖于: 杰玛、彼得、詹姆斯、艾文、杰米

peter 依赖于: 詹姆斯、杰玛、杰西卡、艾文、杰米

杰米依赖于: 伊万

抱歉,如果这已经被问到了。我从现实世界中简化了这个例子,所以我希望它有意义

【问题讨论】:

  • 请附上您的代码。
  • 抱歉,我在回答问题后才看到这个。但是我的代码很乱,所以我一开始没有发布它

标签: javascript node.js arrays recursion reduce


【解决方案1】:

如果你真的想用reduce 这样做,那么:

const getReliedOn = (people, name) =>
    [...(people[name]?.reliesOn || []).reduce(function recur(acc, name) {
        return acc.has(name) ? acc 
            :  (people[name]?.reliesOn || []).reduce(recur, acc.add(name));
    }, new Set([name]))].slice(1);

// Sample input
const people = {'james': { reliesOn: ['gemma','jessica']}, jessica: {reliesOn: ['gemma','peter']}, peter: {reliesOn: ['james','gemma','jessica','ivon','jamie']}, jamie: { reliesOn: ['ivon']}};
// Produce output for each person:
for (let name in people) console.log(name, "=>", ...getReliedOn(people, name));

不带reduce的替代方案,不带递归

这是广度优先搜索 (BFS)。它依赖于这样一个事实:当您迭代一个集合,并且在该迭代期间您向该集合添加值时,循环还将在以后的迭代中访问这些添加的值。所以这个集合就像 BFS 的典型队列:

function getReliedOn(people, name) {
    let names = new Set([name]); // a set with only one entry
    for (let name of names) { // this set will extend while we loop
         if (!people[name]) continue;
         for (let other of people[name].reliesOn) names.add(other);
    }
    // get normal array from set, without original name
    return Array.from(names).slice(1);
}

// Sample input
const people = {'james': { reliesOn: ['gemma','jessica']}, jessica: {reliesOn: ['gemma','peter']}, peter: {reliesOn: ['james','gemma','jessica','ivon','jamie']}, jamie: { reliesOn: ['ivon']}};
// Produce output for each person:
for (let name in people) console.log(name, "=>", ...getReliedOn(people, name));

【讨论】:

  • 嗨@trincot,感谢您的回复。如果我不需要使用reduce,那么最简单的代码是什么?抱歉,我发现这段代码很难理解
  • 抱歉,如果问题的reduce 位过于局限,我已将其从问题中删除。理想情况下,我想要最简单的代码来解决问题
  • 我添加了一个替代解决方案,其中包含一些 cmets。如果您需要更多信息,请告诉我。
  • 非常感谢,替代解决方案非常优雅并且有效!我非常感谢您的帮助@trincot!
【解决方案2】:

我发现在递归时传递一个“备忘录”对象很有用,它可以跟踪我们已经去过的地方以避免循环:

function *getDependenciesRecursive(people, person, alreadyVisited = new Set()) {
  if (alreadyVisited.has(person)) {
    return;
  }
  alreadyVisited.add(person);

  if (!(person in people)) {
    return;
  }

  for (const dependency of people[person].reliesOn) {
    yield dependency;
    yield* getDependenciesRecursive(people, dependency, alreadyVisited);
  }
}

基本上,在这样的方法中,Set 告诉我们要跳过什么。

const reliances = Object
  .entries(people)
  .reduce((results, [person, { reliesOn }]) => Object.assign(results, {
    [person]: [...getDependenciesRecursive(people, person)]
  }), {})

【讨论】:

  • 嗨@jacob,谢谢你的帮助,但是当我运行它时,我得到了未定义:console.log(getDependenciesRecursive(people, 'james'));。我已将代码修改为不使用生成器
猜你喜欢
  • 1970-01-01
  • 2013-12-13
  • 1970-01-01
  • 2011-05-30
  • 1970-01-01
  • 2016-10-03
  • 2014-04-03
  • 2014-08-12
  • 2012-08-27
相关资源
最近更新 更多