【问题标题】:Javascript: Recursion Problem --> Return the longest key value in a deeply nested objectJavascript:递归问题 --> 返回深度嵌套对象中最长的键值
【发布时间】:2019-08-22 21:27:20
【问题描述】:

以下是问题:

// 获取最长的名字

// 编写一个函数,getLongestName,它接受一个对象。该对象代表一个家谱。返回家族中最长的名字。

这是代码,但它返回错误:

let family = {
  'Beverly Marquez': {
    'Nina Rhone': {
      'William Rhodes': null,
      'Paul Nell': null,
      'Sir Paddington the Fourth, of the county Wilstonshire': null
    }
  }
};


function getLongestName (family){

  let longestName = ''; 

  for (let key in family){
    let value = family[key]
    console.log(value)

    if (typeof value === 'object'){
      let descendentLongestName = getLongestName (value)
    }

    else {
      descendentLongestName = value
    }

    if (descendentLongestName.length > longestName.length){
      let longestName = descendentLongestName
    }
  }
  return longestName; 
}


getLongestName(family); // => 'Sir Paddington the Fourth, of the county Wilstonshire'

当我运行上述代码时,我收到以下错误:ReferenceError: descendentLongestName is not defined

我做错了什么?

【问题讨论】:

  • 您需要在声明longestName 的地方声明descendentLongestName,而不是在if 块内。而且你不需要在最后一个if 中使用let,因为longestName 已经声明了。

标签: javascript object recursion nested key


【解决方案1】:

我不知道如何修复您的代码,但我想提出一个新的解决方案。

这个想法是将您的问题分解为两部分:

  • 递归查找嵌套对象的所有键
  • 从字符串数组中找出最长的字符串

let longest = ary => ary
    .reduce((max, x) =>
        x.length > max.length ? x : max, '');

let allKeys = obj => obj
    ? Object.keys(obj).concat(
        ...Object.values(obj).map(allKeys))
    : [];

//

let family = {
    'Beverly Marquez': {
        'Nina Rhone': {
            'William Rhodes': null,
            'Paul Nell': null,
            'Sir Paddington the Fourth, of the county Wilstonshire': null,
        }
    }
};

console.log(longest(allKeys(family)));

【讨论】:

  • sort() 改变数组。建议先对其进行浅拷贝。例如[...ary].sort(/*...*/)
  • @customcommander:是的,我已经用更好的实现替换了longest
【解决方案2】:

让范围是特定于块的,所以 如果你想使用它,那么在块之外声明它,否则使用 var

function getLongestName (family){

  let longestName = ''; 

  for (let key in family){
    let value = family[key]
    console.log(value)
let descendentLongestName='';
    if (typeof value === 'object'){
      descendentLongestName = getLongestName (value)
    }

    else {
      descendentLongestName = value
    }
let longestName;
    if (descendentLongestName && descendentLongestName.length > longestName.length){
     longestName = descendentLongestName
    }
  }
  return longestName; 
}


【讨论】:

  • 当我在上面运行你的代码时,getLongestName(family) 返回 ' '
【解决方案3】:

您可以使用两个部分,一个用于检查键,一个用于获取嵌套对象的键的递归部分。

function getLongestKey(object, keys = []) {
    return Object.keys(object).reduce((r, k) => {
        if (!r || r[0].length < k.length) {
            r = [k];
        } else if (r[0].length === k.length) {
            r.push(k);
        }
        return object[k] && typeof object[k] === 'object'
            ? getLongestKey(object[k], r)
            : r;
    }, undefined)

}

let family = { 'Beverly Marquez': { 'Nina Rhone': { 'William Rhodes': null, 'Paul Nell': null, 'Sir Paddington the Fourth, of the county Wilstonshire': null } } };

console.log(getLongestKey(family));

【讨论】:

    【解决方案4】:

    由于键和它的值可能会争夺最长的字符串,因此在递归函数中使用 Object.entries 可能是有意义的:

    var family = {
        'Beverly Marquez': {
            'Nina Rhone': {
                'William Rhodes': null,
                'Paul Nell': null,
                'Sir Paddington the Fourth, of the county Wilstonshire': null,
            }
        }
    };
    
    const longest = (obj, cur = '') =>
        Object.entries(obj).reduce((max, [key, val]) => {
            const candidate = (val && longest(val, max)) || key;
            return candidate.length > max.length ? candidate : max; }, cur);
            
          
    console.log(longest(family));

    【讨论】:

      【解决方案5】:

      我将从一个简单的函数 traverse 开始 -

      const traverse = function* (t = {})
      { if (t == null) return
        for (const [ name, children ] of Object.entries(t))
        { yield name
          yield* traverse(children)
        }
      }
      
      console.log(Array.from(traverse(family)))
      // [ "Beverly Marquez"
      // , "Nina Rhone"
      // , "William Rhodes"
      // , "Paul Nell"
      // , "Sir Paddington the Fourth, of the county Wilstonshire"
      // ]
      

      这将遍历树与您希望对树的值执行的操作分开。现在我们实现一个简单的longestName 函数-

      const longestName = (t = {}) =>
      { let r = ""
        for (const name of traverse(t))
          if (name.length > r.length)
            r = name
        return r
      }
      
      console.log(longestName(family))
      // Sir Paddington the Fourth, of the county Wilstonshire
      

      如您所见,编写longestName 很容易,因为我们不必同时关注遍历逻辑。

      展开下面的sn-p,在自己的浏览器中验证结果-

      let family = {
        'Beverly Marquez': {
          'Nina Rhone': {
            'William Rhodes': null,
            'Paul Nell': null,
            'Sir Paddington the Fourth, of the county Wilstonshire': null
          }
        }
      }
      
      const traverse = function* (t = {})
      { if (t == null) return
        for (const [ name, children ] of Object.entries(t))
          { yield name
            yield* traverse(children)
          }
      }
      
      const longestName = (t = {}) =>
      { let r = ""
        for (const name of traverse(t))
          if (name.length > r.length)
            r = name
        return r
      }
      
      console.log(longestName(family))
      // Sir Paddington the Fourth, of the county Wilstonshire
      
      console.log(Array.from(traverse(family)))
      // [ "Beverly Marquez"
      // , "Nina Rhone"
      // , "William Rhodes"
      // , "Paul Nell"
      // , "Sir Paddington the Fourth, of the county Wilstonshire"
      // ]

      如果树中有其他数据,您可以看到使用traverse 编写其他函数是多么容易-

      const myTree =
        { name: "Alice"
        , gender: "F"
        , children:
            [ { name: "Bob"
              , gender: "M"
              , children:
                  [ { name: "Charles"
                    , gender: "M"
                    }
                  ]
              }
            ]
        }
      
      const traverse = function* ({ children = [], ...t })
      { yield t
        for (const child of children)
          yield* traverse(child)
      }
      
      const filter = function* (test, t = {})
      { for (const leaf of traverse(t))
          if (test(leaf))
            yield leaf
      }
      
      const byGender = (q = "", t = {}) =>
        filter(node => node.gender === q, t)
      
      console.log(Array.from(byGender("M", myTree)))
      // [ { name: "Bob", gender: "M" }, { name: "Charles", gender: "M" } ]
      
      console.log(Array.from(byGender("F", myTree)))
      // [ { name: "Alice", gender: "F" } ]

      【讨论】:

        【解决方案6】:

        使用for...in 循环遍历family 对象中的键值对。如果该值是一个对象,则使用递归遍历该对象,以查看该对象的键是否比它之前的任何键长。返回最长的键(名称)。

        function getLongestName(family) {
          let longest = "";
          for (let key in family) {
        
            //create initial longest
            if (key.length > longest.length) {
              longest = key;
            } 
        
            let value = family[key];
            
            //if value is an object
            if (typeof value === "object") {
              //use recursion to get the key-values of that value
              let descendant = getLongestName(value);
        
              //if descendant's name is longer than longest, assign it to 'longest'
              if (descendant.length > longest.length) {
                longest = descendant;
              }
            } 
          }
          return longest;
        }
        console.log(getLongestName(family)); 

        【讨论】:

          猜你喜欢
          • 2021-08-10
          • 2020-01-26
          • 2018-11-30
          • 2018-08-16
          • 2018-02-14
          • 1970-01-01
          • 1970-01-01
          • 2022-11-24
          • 1970-01-01
          相关资源
          最近更新 更多