6

I need help traversing a tree structure in a depth first fashion. I can't come up with an algorithm to do it properly.

My input is this:

[
    ["A", "B", "C"],
    ["1", "2"],
    ["a", "b", "c", "d"]
]

The output should take the form:

[
    "A/1/a", "A/1/b", "A/1/c", "A/1/d",
    "A/2/a", "A/2/b", "A/2/c", "A/2/d",
    "B/1/a", "B/1/b", "B/1/c", "B/1/d",
    "B/2/a", "B/2/b", "B/2/c", "B/2/d",
    "C/1/a", "C/1/b", "C/1/c", "C/1/d",
    "C/2/a", "C/2/b", "C/2/c", "C/2/d"
]
Adam
  • 12,236
  • 9
  • 39
  • 44
  • This can be done, but it's a bit tricky. Is there any way you can reorganise your data? You're going to have trouble coordinating which of `A B C` corresponds to which `1 2` and which `a b c d`; your expected data looks like it follows quite a strange rule. +1 for giving expected data, though. – Bojangles Mar 19 '12 at 00:39
  • Sure. I can manipulate the input in any way. What form would be preferable? – Adam Mar 19 '12 at 00:42
  • The easiest way I can think of is to have a child array instead of a sibling array. This is much easier to traverse as a tree-like structure. – Bojangles Mar 19 '12 at 00:45

2 Answers2

7

This should do the job:

function traverse(arr) {
  var first = arr[0];
  var ret = [];
  if (arr.length == 1) {
    for (var i = 0; i < first.length; i++) {
      ret.push(first[i]);
    }
  } else {
    for (var i = 0; i < first.length; i++) {
      var inn = traverse(arr.slice(1));
      for (var j = 0; j < inn.length; j++) {
        ret.push(first[i] + '/' + inn[j]);
      }
    }
  }
  return ret;
}

var inp = [
  ["A", "B", "C"],
  ["1", "2"],
  ["a", "b", "c", "d"]
];

var out = traverse(inp);

console.log(out);
0xdw
  • 3,755
  • 2
  • 25
  • 40
icyrock.com
  • 27,952
  • 4
  • 66
  • 85
2

What you're looking for is the cartesian product of a list of lists, it has been asked before. Borrowing from the accepted answer for that question, you can do this in Javascript 1.7:

function product() {
    return Array.prototype.reduce.call(arguments, function(as, bs) {
        return [a.concat(b) for each (a in as) for each (b in bs)];
    }, [[]]);
};

function convert(lst) {
  var solution = [];
  for (var i = 0; i < lst.length; i++) {
    solution.push(lst[i][0] + "/" + lst[i][1] + "/" + lst[i][2]);
  }
  return solution;
};

convert(product(["A", "B", "C"], ["1", "2"], ["a", "b", "c", "d"]));

> ["A/1/a", "A/1/b", "A/1/c", "A/1/d",
   "A/2/a", "A/2/b", "A/2/c", "A/2/d",
   "B/1/a", "B/1/b", "B/1/c", "B/1/d",
   "B/2/a", "B/2/b", "B/2/c", "B/2/d",
   "C/1/a", "C/1/b", "C/1/c", "C/1/d",
   "C/2/a", "C/2/b", "C/2/c", "C/2/d"]
Community
  • 1
  • 1
Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • I think you should include the permalink, because in theory they can change the accepted answer and post 100 answers more (and it would be pretty hard to find it then) – ajax333221 Mar 19 '12 at 00:59
  • @ajax333221 agreed, I added the permalink as you suggested. – Óscar López Mar 19 '12 at 01:03
  • 1
    This is more elegant and more generalized, but it doesn't solve the specific problem and there is an error in the current version. – Adam Mar 19 '12 at 01:03
  • @Adam I just updated my answer, I made the code a little more clear but apart from that, I couldn't find the error you were talking about, what's it? – Óscar López Mar 19 '12 at 01:29
  • SyntaxError: Unexpected token for – Adam Mar 26 '12 at 16:45
  • @Adam see the quoted [post](http://stackoverflow.com/a/4797658/201359), this solution works for _Javascript 1.7_ – Óscar López Mar 26 '12 at 16:59
  • meh. I know this was an algorithm question but if it doesn't work in chrome stable it's not much use to me. – Adam Mar 26 '12 at 22:47