#javascript #arrays #recursion #filter
Вопрос:
Вот массив, который я получил, и моя цель-отфильтровать весь этот массив объектов и вернуть массив объектов с именем ( даже если он расположен на самом глубоком вложенном уровне детей). Например, если я фильтрую по «Model8», возвращаемое значение моей функции должно быть = [{ name: "Model8", type: "file", path: "/path/to/file" }]
const arr = [
{
name: "Model",
type: "directory",
path: "/path/to/folder",
children: [
{
name: "Model1",
type: "file",
path: "/path/to/file",
children: [
{
name: "Model2",
type: "file",
path: "/path/to/file",
children: [
{
name: "Model3",
type: "file",
path: "/path/to/file",
children: [
{ name: "Model4", type: "file", path: "/path/to/file" },
],
},
],
},
],
},
],
},
{
name: "Inventory",
type: "directory",
path: "/path/to/folder",
children: [{ name: "inventory.yaml", type: "file", path: "/path/to/file" }],
},
{
name: "UI",
type: "directory",
path: "/path/to/folder",
children: [
{ name: "elements", type: "directory", path: "/path/to/file" },
{ name: "viewmodel", type: "directory", path: "/path/to/file" },
{ name: "i18n", type: "directory", path: "/path/to/file" },
{
name: "index.template.html",
type: "file",
path: "/path/to/file",
children: [
{
name: "Model5",
type: "file",
path: "/path/to/file",
children: [
{
name: "Model6",
type: "file",
path: "/path/to/file",
children: [
{
name: "Model7",
type: "file",
path: "/path/to/file",
children: [
{ name: "Model8", type: "file", path: "/path/to/file" },
],
},
],
},
],
},
],
},
],
},
{ name: "DeviceConnector", type: "directory", children: [] },
];
Я придумал 2 варианта :
1)
function searchFilter(searchVal,arr) {
const res = arr.filter(function filteredList(el) {
if (el.children) {
el.children = el.children.filter(filteredList);
}
if (el.name.toLowerCase().includes(searchVal.toLowerCase())) return true;
return res;
});
}
searchFilter("Model8",arr)
Но главная проблема здесь в том, что по какой-то причине я не могу «получить доступ к ‘res’ до инициализации».
2)
function searchFilter(arr, name) {
const searchItem = arr.find((i) => i.name === name);
if (!searchItem) {
return arr.filter((i) => searchFilter(i.children, name));
}
return searchitem;
}
И здесь я не могу опуститься ниже, чем первый итерационный объект массива. Максимальная глубина-это объект со свойством имени «Model3».
Ответ №1:
Мы можем написать универсальную функцию, которая собирает все вложенные значения, соответствующие предоставленному предикату, а затем выполнить поиск по имени поверх него, например так:
const collect = (pred) => (xs = []) =>
xs .flatMap (x => [
... (pred (x) ? [x] : []),
... collect (pred) (x .children)
])
const findByName = (target) =>
collect (({name}) => name == target)
const arr = [{name: "Model", type: "directory", path: "/path/to/folder", children: [{name: "Model1", type: "file", path: "/path/to/file", children: [{name: "Model2", type: "file", path: "/path/to/file", children: [{name: "Model3", type: "file", path: "/path/to/file", children: [{name: "Model4", type: "file", path: "/path/to/file"}]}]}]}]}, {name: "Inventory", type: "directory", path: "/path/to/folder", children: [{name: "inventory.yaml", type: "file", path: "/path/to/file"}]}, {name: "UI", type: "directory", path: "/path/to/folder", children: [{name: "elements", type: "directory", path: "/path/to/file"}, {name: "viewmodel", type: "directory", path: "/path/to/file"}, {name: "i18n", type: "directory", path: "/path/to/file"}, {name: "index.template.html", type: "file", path: "/path/to/file", children: [{name: "Model5", type: "file", path: "/path/to/file", children: [{name: "Model6", type: "file", path: "/path/to/file", children: [{name: "Model7", type: "file", path: "/path/to/file", children: [{name: "Model8", type: "file", path: "/path/to/file"}]}]}]}]}]}, {name: "DeviceConnector", type: "directory", children: []}]
console .log (findByName ('Model8') (arr))
Мы могли бы передать здесь любой предикат. Мы можем использовать сравнение без учета регистра, или поиск подстрок, или комбинацию. Но это не имеет отношения к фактическому обходу дерева, поэтому полезно разделить его на отдельные функции.
Если вам действительно не нравится звонить подобным findByName ('Model8') (arr)
образом (что я предпочитаю в наши дни, но некоторым не нравится), мы могли бы переписать так:
const findByName = (xs, target) =>
collect (({name}) => name == target) (xs)
findByName (arr, 'Model8')
Если бы мы хотели, мы могли бы сделать еще один шаг вперед и передать функцию, чтобы решить, как найти дочерние элементы узла. Здесь мы бы просто прошли (x) => x .children
, но не каждая структура, которую мы хотим использовать, обязательно использует "children"
для этого имя. Эта версия оставлена в качестве упражнения для читателя. 😉