Задать вопрос
Ответы пользователя по тегу Рекурсия
  • Как получить уровни вложенности для всех вложенных объектов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Функции добавить параметр - глубину вложенности.

    Проверяем, чем является значение, если не объект, то возвращаем как есть. Иначе собираем новый объект, в который записываем текущую глубину вложенности и свойства исходного объекта, обработав их значения функцией - делаем рекурсивный вызов с глубиной вложенности, увеличенной на единицу:

    const addDepth = (val, depth = 0) =>
      val instanceof Object
        ? Object.entries(val).reduce((acc, n) => (
            acc[n[0]] = addDepth(n[1], depth + 1),
            acc
          ), { depth })
        : val;

    Но вот вопрос - что если в каких-то объектах уже присутствует свойство с тем же именем, под которым записывается глубина? Как избежать коллизии имён? Можно в качестве имени свойства для глубины использовать символ (на этот раз без reduce'а, без рекурсии, да и копия объекта не создаётся):

    const depthKey = Symbol();
    
    function addDepth(val) {
      for (const stack = [ [ val, 0 ] ]; stack.length;) {
        const [ n, depth ] = stack.pop();
        if (n instanceof Object) {
          n[depthKey] = depth;
          stack.push(...Object.values(n).map(m => [ m, -~depth ]));
        }
      }
    }
    Ответ написан
    Комментировать
  • Что не так с рекурсией?

    0xD34F
    @0xD34F
    Не возвращаете результат рекурсивного вызова - где return перед gen_nums(stop_n, number)?

    А вообще, какого хрена последнее число, в отличие от остальных, не выводится самой функцией? Давайте избавимся от этой шизофрении:

    def gen_nums(max_num, num):
      if num <= max_num:
        print(num)
        gen_nums(max_num, num + 1)
    
    # или
    
    def gen_nums(num):
      if num >= 1:
        gen_nums(num - 1)
        print(num)
    Ответ написан
  • Как извлечь из вложенной структуры элементы удовлетворяющие условию?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Как будем проверять, что значение нам подходит:

    const isValid = n => n.workControl?.includes('intermediate');

    Рекурсия есть:

    const getFromTree = (tree, childrenKey, test) =>
      Array.isArray(tree)
        ? tree.reduce((acc, n) => (
            test(n) && acc.push(n),
            acc.push(...getFromTree(n[childrenKey], childrenKey, test)),
            acc
          ), [])
        : [];
    
    const result = getFromTree(tree, 'content', isValid);

    или

    function* getNestedData(data, test) {
      if (test(data)) {
        yield data;
      }
    
      if (data instanceof Object) {
        for (const k in data) if (Object.hasOwn(data, k)) {
          yield* getNestedData(data[k], test);
        }
      }
    }
    
    const result = [...getNestedData(tree, isValid)];

    Рекурсии нет:

    function getNestedData(data, test) {
      const result = [];
    
      for (const stack = [ data ]; stack.length;) {
        const n = stack.pop();
    
        if (test(n)) {
          result.push(n);
        }
    
        if (n === Object(n)) {
          stack.push(...Object.values(n).reverse());
        }
      }
    
      return result;
    }

    или

    const getFromTree = function*(tree, childrenKey, test) {
      const stack = [];
    
      for (let [ i, arr ] = this(tree); ++i < arr.length || stack.length;) {
        if (i === arr.length) {
          [ i, arr ] = stack.pop();
        } else {
          if (test(arr[i])) {
            yield arr[i];
          }
    
          stack.push([ i, arr ]);
          [ i, arr ] = this(arr[i][childrenKey]);
        }
      }
    }.bind(x => [ -1, x instanceof Array ? x : [] ]);
    Ответ написан
    5 комментариев
  • Как применить рекурсию для получения данных из DOM?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Главный косяк:

    tableValues(element);

    Как вы собираетесь перебирать элемент? Это абсурд. Должно быть element.children/element.childNodes.

    Косяки помельче:

    document.querySelector('form').childNodes

    Поскольку вам нужны только input'ы и select'ы, то не надо перебирать заведомо лишнее - используйте children вместо childNodes. Ну и погуглите, в чём между ними разница.

    if (element.nodeType == 1 && element.nodeName == 'INPUT' || element.nodeName == 'SELECT') {

    Проверка значения nodeType лишняя. Кроме того, вам не помешает разобраться с приоритетом выполнения операторов - nodeType вы тут проверяете только для input'а.

    let result = [];

    Почему эта штука объявляется вне функции, а наполняется внутри? Что, перед каждым вызовом функции будете вручную обнулять результат? Надо создавать массив со значениями внутри функции, и возвращать его как результат её выполнения.

    Ну и конечно всё это делается несколько короче:

    const getValues = el =>
      el instanceof Element
        ? [ 'INPUT', 'SELECT' ].includes(el.tagName)
          ? [ el.value ]
          : [...el.children].flatMap(getValues)
        : [];
    
    // или
    
    const getValues = el =>
      [ 'INPUT', 'SELECT' ].includes(el?.tagName)
        ? [ el.value ]
        : [].flatMap.call(el.children ?? [], getValues);

    const values = getValues(document.querySelector('form'));

    Или ещё короче - если без рекурсии:

    const values = Array.from(document.querySelector('form').elements, n => n.value);

    А вообще, можно и в более общем виде задачу решить - если сделать проверку узла и получение данных из него параметрами функции:

    const getFromDOMNodes = (node, test, getter) =>
      node instanceof Node
        ? Array.prototype.reduce.call(
            node.childNodes,
            (acc, n) => (acc.push(...getFromDOMNodes(n, test, getter)), acc),
            test(node) ? [ getter(node) ] : []
          )
        : [];
    
    // или
    
    function getFromDOMNodes(root, test, getter) {
      const tw = document.createTreeWalker(root);
      const result = [];
      for (let n = null; n = tw.nextNode(); test(n) && result.push(getter(n))) ;
      return result;
    }

    const values = getFromDOMNodes(
      document.querySelector('form'),
      node => [ 'INPUT', 'SELECT' ].includes(node.nodeName),
      node => node.value
    );
    Ответ написан
    1 комментарий
  • Как вывести полный url дерева категорий?

    0xD34F
    @0xD34F
    function getUrls($tree, $url = '') {
      $urls = [];
    
      foreach ($tree as $n) {
        $newUrl = ($url ? $url.'/' : '').$n->url;
        array_push($urls, $newUrl, ...getUrls($n->subcategories ?? [], $newUrl));
      }
    
      return $urls;
    }
    Ответ написан
    2 комментария
  • Как используя рекурсию сконкатенировать только заглавные буквы в слово?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const getNestedItems = (data, test) =>
      data instanceof Object
        ? Object.values(data).flatMap(n => getNestedItems(n, test))
        : test(data) ? [ data ] : [];
    
    const result = getNestedItems(obj, x => /^[A-Z]+$/.test(x)).join('');

    или

    function* getNestedItems(data, test) {
      if (Object(data) === data) {
        for (const k in data) if (Object.hasOwn(data, k)) {
          yield* getNestedItems(data[k], test);
        }
      } else if (test(data)) {
        yield data;
      }
    }
    
    const result = ''.concat(...getNestedItems(obj, x => x?.toUpperCase?.() === x));
    Ответ написан
    4 комментария
  • Почему при глубоком копировании объектов переполняется стек вызовов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const entries = Object.entries(obj);
    for (const entry of entries) {
      if (!isObject(entry)) {
        result = Object.assign(result, Object.fromEntries(entry));
      } else return cloneDeep(entry);

    Что такое entries? - массив, состоящий из массивов.
    Чем, соответственно, является entry? - массивом.
    Что возвращает isObject для массивов? - true.
    То есть, вне зависимости от содержимого конкретного entry произойдёт рекурсивный вызов.
    Всё.
    Ответ написан
    1 комментарий
  • Как вывести ответ в одну строку при рекурсии?

    0xD34F
    @0xD34F
    Вместо вывода текущего результата в консоль возвращайте массив, состоящий из текущего результата и результатов рекурсивного вызова:

    function getDividers(num, divider) {
      return num === 1
        ? []
        : num % divider
          ? getDividers(num, divider + 1)
          : [ divider, ...getDividers(num / divider, divider) ];
    }

    Ну а с полученным массивом можете делать что там вам надо:

    function showDividers(num) {
      if (!Number.isInteger(num) || num < 2) {
        throw 'fuck off';
      }
    
      console.log(`${num} = ${getDividers(num, 2).join(' * ')}`);
    }
    Ответ написан
    1 комментарий
  • Где ошибка при написании полифила для Array.prototype.flat?

    0xD34F
    @0xD34F
    То, что возвращает рекурсивный вызов, тоже в результирующий массив надо засовывать.
    Ответ написан
    Комментировать
  • Как переписать значения во вложенных объектах?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const replaceValues = (val, test, replacer) =>
      val instanceof Array
        ? val.map(n => replaceValues(n, test, replacer))
        : val instanceof Object
          ? Object.fromEntries(Object
              .entries(val)
              .map(([ k, v ]) => [
                k,
                test(k, v)
                  ? replacer(v)
                  : replaceValues(v, test, replacer)
              ])
            )
          : val;

    const newData = replaceValues(
      data,
      k => k.includes('Date'),
      v => v.replace(/(\d+)-(\d+)-/, '$2.$1.')
    );
    Ответ написан
    6 комментариев
  • Как преобразовать имена свойств объекта из kebab-case в camelCase?

    0xD34F
    @0xD34F
    const toCamelCase = val =>
      val instanceof Array
        ? val.map(toCamelCase)
        : val instanceof Object
          ? Object.fromEntries(Object
              .entries(val)
              .map(n => [
                n[0].replace(/_+(.)/g, (m, g1) => g1.toUpperCase()),
                toCamelCase(n[1]),
              ])
            )
          : val;
    Ответ написан
    1 комментарий
  • Как обойти дерево и составить все его пути (url категорий)?

    0xD34F
    @0xD34F
    function getURLs($arr, $path = []) {
      $urls = [];
    
      foreach ($arr as $item) {
        array_push($path, $item['slug']);
        array_push($urls, '/'.implode('/', $path).'/', ...getURLs($item['children'], $path));
        array_pop($path);
      }
    
      return $urls;
    }
    Ответ написан
  • Как рекурсивно удалить текстовые узлы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Во-первых - вы передаёте в функцию строку и используете её в качестве селектора. Как быть с элементами, которые селектору не соответствуют? Они не будут найдены. Пусть функция сразу получает узел DOM-дерева.

    Во-вторых - вы обходите childNodes, который представляет собой динамический NodeList и одновременно пытаетесь его модифицировать. Удаляя один узел, вы пропускаете следующий - т.е., если несколько текстовых узлов идут подряд, то удалён будет только каждый второй; а те нетекстовые узлы, которые расположены после текстовых, тут никакой рекурсии не случится, их содержимое вообще никак не будет обработано. Надо делать копию childNodes, и перебирать её. Или вместо for...of использовать цикл со счётчиком, при удалении узла счётчик увеличиваться не должен. Или перебирать childNodes от конца к началу - тоже цикл со счётчиком или reduceRight (да, это будет использование метода не совсем по назначению).

    В-третьих - какой-то бред с nextElementSibling, не знаю, как это комментировать. Надо было просто вызвать функцию, передав ей текущий элемент.

    В четвёртых:

    const deleteTextNodes = node =>
      node.nodeType === Node.TEXT_NODE
        ? node.remove()
        : [...node.childNodes].forEach(deleteTextNodes);
    
    // или
    
    function deleteTextNodes(node) {
      if (node instanceof Text) {
        node.replaceWith();
      } else {
        for (let i = node.childNodes.length; i--;) {
          deleteTextNodes(node.childNodes[i]);
        }
      }
    }
    Ответ написан
  • Рекурсивное умножение разрядов целого числа, как узнать количество вызовов функции?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Проверяем число, если однозначное - возвращаем 0; в противном случае возвращаем сумму единицы и результата рекурсивного вызова:

    const multiply = num =>
      num > 9
        ? 1 + multiply([...`${num}`].reduce((acc, n) => acc * n))
        : 0;
    Ответ написан
    Комментировать
  • Как рекурсивно получить значения Фибоначчи, которые меньше заданного числа?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function fib(max) {
      const next = (p1, p2) => p2 <= max ? ',' + p2 + next(p1 + p2, p1) : '';
      return 0 + next(1, 1);
    }

    fib(0)   // "0"
    fib(1)   // "0,1,1"
    fib(10)  // "0,1,1,2,3,5,8"
    fib(45)  // "0,1,1,2,3,5,8,13,21,34"
    fib(100) // "0,1,1,2,3,5,8,13,21,34,55,89"
    Ответ написан
    Комментировать
  • Как определить максимальную вложенность массивов в массиве?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Как можно дополнить мой код, чтобы он проверял то, что требуется?

    Дополнить - никак. Надо переписать. К единице надо плюсовать не все результаты рекурсивных вызовов, а только один, наибольший.

    И можно как-нибудь без объявления переменной n внутри функции?

    Можно:

    const getMaxDepth = arr =>
      Array.isArray(arr)
        ? 1 + Math.max(0, ...arr.map(getMaxDepth))
        : 0;

    console.log(getMaxDepth([ 1, [ 2 ], [ [ 3 ] ], [ [ [ 4 ] ] ] ])); // 4
    console.log(getMaxDepth([])); // 1
    console.log(getMaxDepth(666)); // 0
    Ответ написан
    2 комментария
  • Как рекурсивно посчитать количество строк во вложенной структуре?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Функция должна возвращать результат обработки переданного ей значения + сумму результатов обработки вложенных значений (если таковые есть):

    const sum = (data, getVal) => Object
      .values(data instanceof Object ? data : {})
      .reduce((acc, n) => acc + sum(n, getVal), getVal(data));

    sum([ 1, 2, 3, '1', [ '2', 4, [ '3' ] ], '4' ], x => +(typeof x === 'string')) // 4
    sum({ a: { b: [ NaN, Infinity ], c: 123456 } }, x => +(typeof x === 'number')) // 3
    Ответ написан
    Комментировать
  • Как сделать правильно рекурсию?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function xxx(key) {
      let val = entityTree[key];
      const nextKey = Array.isArray(val) && (val = [...val], val.pop());
      return val ? [].concat(key, val, xxx(nextKey)) : [];
    }
    Ответ написан
    3 комментария
  • Почему такое странное поведение переменной после рекурсии?

    0xD34F
    @0xD34F
    Потому что при передаче списка в качестве параметра копируется не он сам, а ссылка на него. При рекурсивном вызове вместо mass передавайте mass.copy().
    Ответ написан
    4 комментария
  • Как при рекурсивном переборе древовидных объектов вызывать fetch на каждый item и на выходе получать измененный массив?

    0xD34F
    @0xD34F Куратор тега JavaScript
    async function addOptions(arr) {
      const result = [];
    
      for (const item of arr) {
        result.push({
          ...item,
          options: await fetchOptions(item.id),
          children: await addOptions(item.children),
        });
      }
    
      return result;
    }
    Ответ написан
    Комментировать