Поиск вложенного массива объектов и возврат полных родительских элементов в качестве результатов в JavaScript

#javascript

#javascript

Вопрос:

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

 const myObj = [
  {
    name: '1',
    pages: [
      {
        name: '1.1',
        pages: []
      },
      {
        name: '1.2',
        pages: []
      },
    ]
  },
  {
    name: '2',
    pages: []
  },
  {
    name: '3',
    pages: []
  }
]

function searchPages(searchQuery, obj) {
  let searchResults = [];
  for (let i = 0; i < obj.length; i  ) {
    let item = searchString(obj[i], searchQuery);

    if (item) {
      searchResults.push(item);
    }
  }
  return searchResults;
}

function searchString(obj, string) {
  if (obj.name.includes(string)) {
    return obj;
  }

  for (let i = 0; i < obj.pages.length; i  ) {
    const possibleResult = searchString(obj.pages[i], string);
    if (possibleResult) {
      return possibleResu<
    }
  }
}

let searchResults = searchPages('1.1', myObj);
console.log(searchResults);  

Это выполняет правильный поиск во вложенном массиве и дает правильный результат:

   {
    "name": "1.1",
    "pages": []
  }
  

Но я хотел бы вернуть весь родительский объект, а не только дочерний объект. Итак, ожидаемый результат таков:

   {
    name: '1',
    pages: [
      {
        name: '1.1',
        pages: []
      },
      {
        name: '1.2',
        pages: []
      },
    ]
  }
  

Как я могу изменить свою функцию для достижения этой цели?

Имейте в виду, что это всего лишь небольшой объект только для удобства чтения. Мой фактический объект будет иметь гораздо больше уровней и свойств.

Ответ №1:

Вы можете использовать рекурсивный подход и проверить, имеют ли вложенные массивы нужное имя.

 function searchPages(array, string) {
    const find = ({ name, pages }) => name.includes(string) || pages amp;amp; pages.some(find);
    return array.filter(find);
}

const
    data = [{ name: '1', pages: [{ name: '1.1', pages: [] }, { name: '1.2', pages: [] }] }, { name: '2', pages: [] }, { name: '3', pages: [] }],
    searchResults = searchPages(data, '1.1');

console.log(searchResults);  
 .as-console-wrapper { max-height: 100% !important; top: 0; }  

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

1. Спасибо за ваш ответ! кажется, это работает для меня, но есть ли способ изменить это, чтобы оно включало частичные строки? как то, что я сделал в своей функции obj.name.includes(string)

2. вы можете заменить проверку на includes . пожалуйста, смотрите Редактирование.

Ответ №2:

Вот мой подход к .filter -ing myObj и .find -ing вложенным страницам с заданным именем

 const myObj = [
  {
    name: '1',
    pages: [
      {
        name: '1.1',
        pages: []
      },
      {
        name: '1.2',
        pages: []
      },
    ]
  },
  {
    name: '2',
    pages: []
  },
  {
    name: '3',
    pages: []
  }
];

const searchPages = (name, arr) => arr.filter(
  ({ pages }) => pages.find(page => page.name === name)
) 

let searchResults = searchPages('1.1', myObj);
console.log(searchResults);  

Ответ №3:

Если вы хотите вернуть родительский object объект вместо искомого object , вам просто нужно изменить searchString() реализацию, чтобы она принимала родительский объект в качестве третьего параметра, а затем возвращала его, если вы находите желаемый string :

 function searchPages(searchQuery, obj) {
  let searchResults = [];
  for (let i = 0; i < obj.length; i  ) {
    let item = searchString(obj[i], searchQuery, obj);

    if (item) {
      searchResults.push(item);
    }
  }
  return searchResults;
}

function searchString(obj, string, parent) {
  if (obj.name.includes(string)) {
    return parent;
  }

  for (let i = 0; i < obj.pages.length; i  ) {
    const possibleResult = searchString(obj.pages[i], string, obj);
    if (possibleResult) {
      return possibleResu<
    }
  }
}
  

Таким образом, вы всегда будете учитывать родительский элемент.

ДЕМОНСТРАЦИЯ:

 const myObj = [
  {
    name: '1',
    pages: [
      {
        name: '1.1',
        pages: [
          {
            name: '1.1.1',
            pages: []
          }
        ]
      },
      {
        name: '1.2',
        pages: []
      },
    ]
  },
  {
    name: '2',
    pages: []
  },
  {
    name: '3',
    pages: []
  }
]

function searchPages(searchQuery, obj) {
  let searchResults = [];
  for (let i = 0; i < obj.length; i  ) {
    let item = searchString(obj[i], searchQuery, obj);

    if (item) {
      searchResults.push(item);
    }
  }
  return searchResults;
}

function searchString(obj, string, parent) {
  if (obj.name.includes(string)) {
    return parent;
  }

  for (let i = 0; i < obj.pages.length; i  ) {
    const possibleResult = searchString(obj.pages[i], string, obj);
    if (possibleResult) {
      return possibleResu<
    }
  }
}

let searchResults = searchPages('1.1.1', myObj);
console.log(searchResults);  

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

1. спасибо за ответ! но это не работает с массивами, вложенными более чем на 2 уровня

2. @cup_of можете ли вы воспроизвести это на примере?

3. да, я отредактировал ваш ответ, он вернет только своего непосредственного родителя

4. @cup_of поскольку это родительский объект, вы сказали, что вам нужно вернуть родительский, а не текущий объект.

Ответ №4:

Вот один из возможных подходов, использующий .filter для верхнего массива, за которым следуют рекурсивные вызовы .some :

 const myObj = [
  {
    name: '1',
    pages: [
      {
        name: '1.1',
        pages: []
      },
      {
        name: '1.2',
        pages: []
      },
    ]
  },
  {
    name: '2',
    pages: []
  },
  {
    name: '3',
    pages: []
  }
];

const searchPages = (nameToFind, obj) => obj.filter(pageContainsName(nameToFind));
const pageContainsName = nameToFind => ({ name, pages }) => (
  name === nameToFind || pages.some(pageContainsName(nameToFind))
);
let searchResults = searchPages('1.1', myObj);
console.log(searchResults);