Рекурсивная функция не переходит к следующему элементу в foreach

Мне кажется, что код второго элемента в items никогда не достигается в цикле for.

Как исправить этот метод?

class Item {
  items: Item[] = [];
  constructor(public id: string) {}
}

function getItembyIdRecursively(id: string, items: Item[]): Item | undefined {
  for (const item of items) {
    if (item.id === id) {
      return item;
    } else {
      return getItembyIdRecursively(id, item.items);
    }
  }
  return undefined;
}

const item1 = new Item("1");
const item2 = new Item("2");
item1.items.push(new Item("1.2"));
const items = [item1, item2];

const foundItem = getItembyIdRecursively("2", items);

console.info(foundItem.id);

Образец репродукции вы можете найти здесь:

ссылка на репродукцию образца

Пройдитесь по коду. Что будет возвращено, скажем, когда метод запишет один раз? Что возвращает самый верхний вызов, если рекурсивный вызов возвращает значение?

Dave Newton 08.05.2024 14:36

Итак, проблема в том, что вы безоговорочно возвращаете результат рекурсивного вызова; вам следует проверить, есть ли это undefined, и не возвращать его, если да. Посмотрите ссылку на игровую площадку, которая теперь работает так, как вы хотите. Это полностью решает вопрос? Если да, то я напишу ответ; если нет, то что мне не хватает?

jcalz 08.05.2024 14:43
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
2
63
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема в вашем операторе else: вы возвращаете результат во всех случаях, даже если это undefined. По сути, поскольку у первого Item есть дочерние элементы, вы возвращаете результат поиска в его дочерних элементах, а поскольку идентификатор не совпадает, вы получаете undefined.

function getItembyIdRecursively(id: string, items: Item[]) : Item  | undefined
{
  for(const item of items)
  {
    if (item.id === id)
    {
      return item;
    }
    else
    {
      const elem = getItembyIdRecursively(id, item.items);
      if (elem) {
        return elem;
      }
    }
  }
  return undefined;
}

Это то же самое, что и это.

function getItembyIdRecursively(id: string, items: Item[]) : Item  | undefined
{
  for(const item of items)
  {
    if (item.id === id) return item;
    const elem = getItembyIdRecursively(id, item.items);
    if (elem !== undefined) return elem;
  }
  return undefined;
}

генераторы

Я думаю, что генераторы предлагают естественный способ выражения функций поиска или обхода. Мы видим, что генератору не требуется дополнительная проверка if, прежде чем продолжить yield*. Еще одним преимуществом является то, что потребителю всегда гарантирована ценность.

function getItem(id: string, items: Item[]): undefined | Item {

  function* find(items: Item[]): Generator<Item> {
    for (const item of items) {
      if (item.id == id) yield item
      yield* find(item.items)         // unconditional
    }
  }

  for (const match of find(items))
    return match                      // unconditional
}
class Item {
  items: Item[]
  constructor(
    public id: string,
    items?: Item[],
  ) {
    this.items = items ?? []
  }
}

const example = [
  new Item("A", [
    new Item("B", [new Item("C"), new Item("D", [new Item("E")])]),
    new Item("F"),
  ]),
]

Запустите этот пример на игровой площадке для машинописных текстов

console.info(getItem("D", example)) // Item: { id: "D", items: [ Item: { id: "E", items: [] } ] }
console.info(getItem("X", example)) // undefined

общая находка

Вместо того, чтобы встраивать генератор в функцию getItem, мы можем определить find как отдельную функцию, независимую от типа Item

function* find<T>(
  values: Iterable<T>,
  matchFn: (value: T) => boolean,
  recurFn?: (value: T) => Iterable<T>,
): Generator<T> {
  for (const value of values) {
    if (matchFn(value)) yield value
    if (recurFn) yield* find(recurFn(value), matchFn, recurFn)
  }
}

Теперь find можно использовать повторно везде, где нам нужна эта функциональность, например getItem -

function getItem(id: string, items: Item[]): undefined | Item {
  for (const match of find(  // find..
    items,                   // iterable
    i => i.id == id,         // match
    i => i.items,            // recur
  ))
    return match             // unconditional
}

Запустите этот пример на игровой площадке для машинописных текстов

console.info(getItem("D", example)) // Item: { id: "D", items: [ Item: { id: "E", items: [] } ] }
console.info(getItem("X", example)) // undefined

удобство и контроль: $0,99

Генераторы дают нам всевозможную мощность и гибкость. getItem возвращает только первый результат, что немедленно завершает работу генератора и прекращает сканирование других элементов. С небольшим изменением мы могли бы написать getItems, который возвращает все результаты:

function getItems(ids: string[], items: Item[]): Item[] {
  return Array.from(find(          // unconditional
    items,
    i => ids.includes(i.id),
    i => i.items,
  ))
}

Запустите этот пример на игровой площадке для машинописных текстов

console.info(getItems(["E", "F"], example)) // [ Item: { id: "E" }, Item: { id: "F" } ]
console.info(getItems(["X"], example)) // []

Другие вопросы по теме