Обнаружить и исправить недочёты в функции распределения категорий

Object.assign меняет исходный catList, а присваивание children мутирует вложенного родителя. Создайте копии категорий и массивов; при переносе категории удалите прежнюю связь перед добавлением новой.

Короткий ответ

Что ответить на собеседовании

Object.assign меняет исходный catList, а присваивание children мутирует вложенного родителя. Создайте копии категорий и массивов; при переносе категории удалите прежнюю связь перед добавлением новой.

Подробный разбор

Ответ с пояснениями

Условие

В данном фрагменте JavaScript‑кода реализована функция, предназначенная для объединения списка категорий с новой категорией и обновления структуры дерева категорий. Однако в реализации присутствуют логические ошибки, которые приводят к некорректному изменению исходных данных.

export const distributionCategories = (catList, rootArr, newCat) => {
    Object.assign(catList, newCat);

    const rootSet = new Set(rootArr);

    Object.keys(newCat).map(Number).forEach((categoryId) => {
        const curCat = catList[categoryId];
        if (curCat.level == 0) {
            rootSet.add(categoryId);
        } else {
            const childSet = new Set(catList[curCat.parent].children);
            childSet.add(categoryId);
            catList[curCat.parent].children = Array.from(childSet);
        }
    });
    return [catList, rootSet];
};

Ваша задача — проанализировать код, выявить причины неправильного поведения и предложить исправления, сохранив исходную сигнатуру функции и её общую логику.

Ответ

Object.assign меняет исходный catList, а присваивание children мутирует вложенного родителя. Создайте копии категорий и массивов; при переносе категории удалите прежнюю связь перед добавлением новой.

Для числовых ID и полного описания обновляемых категорий:

export const distributionCategories = (catList, rootArr, newCat) => {
  const merged = { ...catList, ...newCat };
  const result = Object.fromEntries(Object.entries(merged).map(([id, cat]) =>
    [id, { ...cat, children: [...(cat.children ?? catList[id]?.children ?? [])] }]));
  const roots = new Set(rootArr.map(Number));
  for (const key of Object.keys(newCat)) {
    const id = Number(key), current = result[key];
    if (!Number.isSafeInteger(id)) throw new Error('Некорректный ID');
    if (current.level !== 0 && !Object.hasOwn(result, current.parent)) throw new Error('Нет родителя');
    const seen = new Set([id]);
    let cursor = current;
    while (cursor.level !== 0) {
      const parentId = Number(cursor.parent);
      if (seen.has(parentId) || !Object.hasOwn(result, parentId)) throw new Error('Некорректное дерево');
      seen.add(parentId); cursor = result[parentId];
    }
    roots.delete(id);
    for (const cat of Object.values(result)) cat.children = cat.children.filter(child => Number(child) !== id);
    if (current.level === 0) roots.add(id);
    else result[current.parent].children.push(id);
  }
  return [result, roots];
};

Возвращаемый второй элемент остаётся Set, как в исходном контракте. Чужие объекты не меняются; некорректные родители и циклы отвергаются. Частичные patch-объекты потребовали бы отдельного поэлементного merge полей.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее