2

I have an array like:

"pages": [{
    "key": "1",
    "pages": [{
        "key": "2",
        "pages": [{
            "key": "3"
        }]
    },
        {
            "key": "4",
            "pages": [{
                "key": "5"
            }]

        }]
}]

where key 1 and 4 are at same level and 1 contains 2 which contains 3 and the key 4 contains 5. The result I want is in the order [3,2,5,1,4]. I have tried the following recursion but i am not able to get the correct order.

 function fnGetAll (oTopDetailPage, array) {
    var i;

    for (i=0; i<oTopDetailPage.length; i++) {
        array.push(oTopDetailPage[i]);
        if(oTopDetailPage[i].pages) {
            fnGetAllSubPages(oTopDetailPage[i].pages, array);
        }
    }
    return array;
}
Junius L
  • 15,881
  • 6
  • 52
  • 96
boomcode
  • 399
  • 1
  • 14
  • 1
    The output you are going for doesn't really make sense. Can you change what you are expecting? Maybe an object with the parent page and then the children like `{ 1: [2, 3], 4: [5] }`. Why do you need to output you are requesting ? – pizzarob Apr 21 '17 at 20:35
  • If you add another page, with say, a "key" of "6", within the pages property of the object with a "key" of "1", where would that fall in your output and why? – Heretic Monkey Apr 21 '17 at 20:42
  • why does `5` come before `1`? – Junius L Apr 21 '17 at 20:42
  • jagged looking array. See [How to initialize a jagged array in JavaScript?](http://stackoverflow.com/questions/5939504/how-to-initialize-a-jagged-array-in-javascript) – Ronnie Royston Apr 21 '17 at 20:43
  • 2
    To those wondering where the order comes from: it looks like boomcode is trying to get each level from lowest to highest: Level 1: [1,4], Level 2: [2,5], Level 3 [3], Level 3, Level 2, Level 1 -> 3,2,5,1,4 – Patrick Barr Apr 21 '17 at 20:46
  • 1
    @PatrickBarr from the code above 1 and 4 are not on the same level. 4 is nested inside 1. – Junius L Apr 21 '17 at 21:00

2 Answers2

5

If you want a Depth-first search, you could iterate the children first and then take the actual key.

The result is an array, which is a bit different, then the given array.

function getDepthFirst(object) {
    var result = [];
    object.pages.forEach(function iter(a) {
        Array.isArray(a.pages) && a.pages.forEach(iter);
        result.push(a.key);
    });
    return result;
}

var data = { pages: [{ key: 1, pages: [{ key: 2, pages: [{ key: 3 }] }, { key: 4, pages: [{ key: 5 }] }] }] };
  
console.log(getDepthFirst(data)); // [3, 2, 5, 4, 1]

Addendum for getting a reverse level order traversal result of [3, 5, 2, 4, 1], with a temporary array, which collects all data from the same level and return an array with items from all levels, starting from the highest to the lowest.

The callback uses a closure over the actual level.

function getData(object) {
    var temp = [];
    object.pages.forEach(function iter(level) {
        return function (a) {
            Array.isArray(a.pages) && a.pages.forEach(iter(level + 1));
            temp[level] = temp[level] || [];
            temp[level].push(a.key);
        };
    }(0));
    return temp.reduceRight(function (r, a) {
        return r.concat(a);
    });
}

var data = { pages: [{ key: 1, pages: [{ key: 2, pages: [{ key: 3 }] }, { key: 4, pages: [{ key: 5 }] }] }] };

console.log(getData(data)); // [3, 5, 2, 4, 1]
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

Your root container is a little weird because it's invalid javascript. I'm assuming it's {pages: ... } with enclosing {} but even that doesn't make sense because the root container doesn't contain a key property.

You should first fix your nodes such that you have a uniform construction, eg

type Node = Node { key: String, pages: [Node] }

Then implementing your depth-first search is trivial

const dfs = ({key, pages = []}) =>
  [...pages.reduce((acc, p) => acc.concat(dfs(p)), []), key]
  
const data = {
  "key": "1",
  "pages": [{
      "key": "2",
      "pages": [{
          "key": "3"
      }]
  },
  {
      "key": "4",
      "pages": [{
          "key": "5"
      }]
  }]
}
  
console.log(dfs(data))
// [ '3', '2', '5', '4', '1' ]

If you were constructing data by-hand, don't. Instead, I suggest you make a simple constructor to build the data uniformly. Because each node is now guaranteed to have a key and pages property, we can remove the default parameter value for pages = [] in dfs. This is better because we can avoid any defensive programming that might try to accommodate for the missing property.

const dfs = ({key, pages}) =>
  [...pages.reduce((acc, p) => acc.concat(dfs(p)), []), key]
  

const makeNode = (key, ...pages) => ({key, pages})

const data =
  makeNode('1',
    makeNode('2',
      makeNode('3')),
    makeNode('4',
      makeNode('5')))
  
console.log(dfs(data))
// [ '3', '2', '5', '4', '1' ]
Mulan
  • 129,518
  • 31
  • 228
  • 259