Javascript: проблема рекурсии -> Возвращает самое длинное значение ключа в глубоко вложенном объекте

#javascript #object #recursion #nested #key

#javascript #объект #рекурсия #вложенный #Клавиша

Вопрос:

Ниже приведена проблема:

// Получить самое длинное имя

// Напишите функцию 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 не определено

Что я сделал не так?

Комментарии:

1. Вам нужно объявить, descendentLongestName где вы объявляете longestName , а не внутри if блока. И вам не нужно let в этом последнем, if потому что longestName оно уже объявлено.

Ответ №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)));  

Комментарии:

1. sort() изменяет массив. Может быть целесообразно сначала скопировать его неглубоко. например [...ary].sort(/*...*/)

2. @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 amp;amp; descendentLongestName.length > longestName.length){
     longestName = descendentLongestName
    }
  }
  return longestName; 
}


  

Комментарии:

1. когда я запускаю ваш приведенный выше код, 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] amp;amp; 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 amp;amp; 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 теперь легко, поскольку нам не нужно одновременно заниматься логикой обхода.

Разверните приведенный ниже фрагмент, чтобы проверить результаты в вашем собственном браузере —

 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));