Цикл структуры файловой системы в моем объекте, чтобы получить все файлы

Я получаю объект Javascript с моего сервера, который изображает файловую систему. Теперь я хочу получить путь ко всем файлам в системе, например, к конечным точкам дерева.

Пример структуры файла:

└── pages
    └── services
        └── project            
            │── headline
            │── test
            │   └── text
            │   └── picture 
            │── text

Читаемый JSON:

{
"path":"/pages/services/project",
"is_dir":true,
"children":[
  {
     "path":"/pages/services/project/headline",
     "is_dir":false,
     "children":[

     ]
  },
  {
     "path":"/pages/services/project/text",
     "is_dir":false,
     "children":[

     ]
  },
  {
     "path":"/pages/services/project/test/",
     "is_dir":true,
     "children":[
        {
           "path":"/pages/services/project/test/text",
           "is_dir":false,
           "children":[

           ]
        },
        {
           "path":"/pages/services/project/test/picture",
           "is_dir":false,
           "children":[

           ]
        }
     ]
  }

]}

Ожидаемый результат:

/pages/services/project/headline
/pages/services/project/text
/pages/services/project/test/text
/pages/services/project/test/picture

Я немного поиграл с рекурсией и сделал глупую функцию, которая работает, когда у режиссера только один ребенок. Моя проблема в том, что я не могу понять, как справиться с большим количеством детей. Есть ли способ перебрать каждого ребенка?

Вот мой код:

var json = {"path":"/pages/services/project", "is_dir":true, "children":[{"path":"/pages/services/project/headline","is_dir":false,"children":[]},{"path":"/pages/services/project/text","is_dir":false,"children":[]},
{"path":"/pages/services/project/test/","is_dir":true,"children":[{"path":"/pages/services/project/test/text","is_dir":false,"children":[]},
{"path":"/pages/services/project/test/picture","is_dir":false,"children":[]}]}]};

json.children.forEach(function (child) {
 out(goToDeepestPoint(child).path);
});



function goToDeepestPoint(node) {
    if (node.is_dir)
        return goToDeepestPoint(node.children[0]);
    else 
        return node;
}

function out()
{
    var args = Array.prototype.slice.call(arguments, 0);
    document.getElementById('output').innerHTML += args.join(" ") + "\n";
}
<pre id="output"></pre>

4 ответа

Решение

Я был занят игрой с вашим оригинальным вопросом и получил это работать:

goToDeepestPoint(json);

function goToDeepestPoint(node) {
    if (node.is_dir)
        node.children.forEach(function (child) {
    goToDeepestPoint(child);
});
    else 
        return out(node.path);
}

Может не подходить в свете ваших правок, но позор тратить их впустую!

Я поделюсь своим ответом, потому что он имеет отношение к тому, над чем я сейчас работаю - он требует более функционального подхода, потому что это именно то, что я изучаю

Постоянные итераторы

Итераторы с состоянием в JavaScript делают меня грустным, поэтому мы можем реализовать постоянный интерфейс итератора, используя наш собственный Yield а также Return типы. Memo Тип используется, но это просто деталь оптимизации.

const Memo = (f, memo) => () =>
  memo === undefined
    ? (memo = f (), memo)
    : memo

const Yield = (value, next = Return) =>
  ({ done: false, value, next: Memo (next) })
  
const Return = value =>
  ({ done: true, value })
  
// example use
const ArrayIterator = (xs = []) =>
  xs.length === 0
    ? Return ()
    : Yield (xs [0], () => ArrayIterator (xs.slice (1)))
    
const it =
  ArrayIterator ([1,2,3])

console.log (it.value) // 1
console.log (it.value) // 1
console.log (it.next () .value) // 2
console.log (it.next () .value) // 2

Теперь, если необходимо создать адаптер для взаимодействия с собственными генераторами JavaScript, мы можем создать Generator тип

Правда, это не супер интересно, но оно демонстрирует необходимую функциональность

const Generator = function* (it = Return ())
  {
    while (it.done === false)
      (yield it.value, it = it.next ())
    return it.value
  }

Array.from (Generator (ArrayIterator ([1,2,3])))
// => [1,2,3]

Наши настойчивые итераторы открывают двери для более захватывающих вещей, как это

const MappedIterator = (f = x => x, it = Return ()) =>
  it.done
    ? Return ()
    : Yield (f (it.value), () => MappedIterator (f, it.next ()))

const ConcatIterator = (x = Return (), y = Return) =>
  x.done
    ? y ()
    : Yield (x.value, () => ConcatIterator (x.next (), y))

const it =
  MappedIterator (x => x * x, ArrayIterator ([1,2,3]))

Array.from (Generator (it))                      // => [ 1, 4, 9 ]
Array.from (Generator (ConcatIterator (it, it))) // => [ 1, 4, 9, 1, 4, 9 ]

Чистое выражение

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

const FlatMappedIterator = (f, it = Return ()) =>
  it.done
    ? Return ()
    : ConcatIterator (f (it.value), () => FlatMappedIterator (f, it.next ()))

const MyTreeIterator = node =>
  node === undefined
    ? Return ()
    : node.is_dir
      ? FlatMappedIterator (MyTreeIterator, ArrayIterator (node.children))
      : Yield (node.path)

Конечно, ответ неполон без демонстрации рабочего кода

const Memo = (f, memo) => () =>
  memo === undefined
    ? (memo = f (), memo)
    : memo

const Yield = (value, next = Return) =>
  ({ done: false, value, next: Memo (next) })
  
const Return = value =>
  ({ done: true, value })

// -------------------------------------------------------------------
const ArrayIterator = (xs = []) =>
  xs.length === 0
    ? Return ()
    : Yield (xs [0], () => ArrayIterator (xs.slice (1)))
    
const ConcatIterator = (x = Return (), y = Return) =>
  x.done
    ? y ()
    : Yield (x.value, () => ConcatIterator (x.next (), y))
    
const FlatMappedIterator = (f, it = Return ()) =>
  it.done
    ? Return ()
    : ConcatIterator (f (it.value), () => FlatMappedIterator (f, it.next ()))

const Generator = function* (it = Return ())
  {
    while (it.done === false)
      (yield it.value, it = it.next ())
    return it.value
  }
  
// -------------------------------------------------------------------
const MyTreeIterator = node =>
  node === undefined
    ? Return ()
    : node.is_dir
      ? FlatMappedIterator (MyTreeIterator, ArrayIterator (node.children))
      : Yield (node.path)

const data =
  {path:'/pages/services/project', is_dir:true, children:[
    {path:'/pages/services/project/headline',is_dir:false,children:[]},
    {path:'/pages/services/project/text',is_dir:false,children:[]},
    {path:'/pages/services/project/test/',is_dir:true,children:[
      {path:'/pages/services/project/test/text',is_dir:false,children:[]},
      {path:'/pages/services/project/test/picture',is_dir:false,children:[]}
    ]}
  ]}

// -------------------------------------------------------------------
// example use of generator around our custom persistent iterator
for (const path of Generator (MyTreeIterator (data)))
  {
    const elem = document.createElement ('p')
    elem.textContent = path
    document.body.appendChild (elem)
  }

var json = {"path":"/pages/services/project", "is_dir":true, "children":[{"path":"/pages/services/project/headline","is_dir":false,"children":[]},{"path":"/pages/services/project/text","is_dir":false,"children":[]},
{"path":"/pages/services/project/test/","is_dir":true,"children":[{"path":"/pages/services/project/test/text","is_dir":false,"children":[]},
{"path":"/pages/services/project/test/picture","is_dir":false,"children":[]}]}]};

function getPaths(obj){
    let foundPaths = [];
    if(obj.children.length > 0){
        obj.children.forEach(function (element){
           let childPaths = getPaths(element);
           foundPaths = foundPaths.concat(childPaths);
        });
        return foundPaths;
   } else {
      foundPaths.push(obj.path);
      return foundPaths;
   }
}

let paths = getPaths(json);

document.getElementById('output').innerHTML += paths.join("\n");
<pre id="output"></pre>

Рабочий раствор:

var json = {"path":"/pages/services/project", "is_dir":true, "children":[{"path":"/pages/services/project/headline","is_dir":false,"children":[]},{"path":"/pages/services/project/text","is_dir":false,"children":[]},
{"path":"/pages/services/project/test/","is_dir":true,"children":[{"path":"/pages/services/project/test/text","is_dir":false,"children":[]},
{"path":"/pages/services/project/test/picture","is_dir":false,"children":[]}]}]};

json.children.forEach(function (child) {
    goToDeepestPoint(child);
});



function goToDeepestPoint(node) {
    if (node.is_dir){
      for(var i=0;i<node.children.length;i++){
        goToDeepestPoint(node.children[i]);
      }
    }        
    else {
        out(node.path);
    }
}

function out()
{
    var args = Array.prototype.slice.call(arguments, 0);
    document.getElementById('output').innerHTML += args.join(" ") + "\n";
}
Другие вопросы по тегам