Reputation: 257
I have this object which its keys are guaranteed sorted and will be used for the operation. And each of its value is a 2d array.
var obj = {
"0": [
[0, 1], [0, 3], [0, 4]
],
"1": [
[1, 2], [1, 3]
],
"2": [
[2, 3], [2, 5]
],
"3": [
[3, 4], [3, 6]
],
"5": [
[5, 6]
],
"6": [
[6, 5]
]
}
I am trying to concatenate them and for each of its last value of the array is the next index of the object. So, my expected result is an array like this,
The pattern is, I have to find a way from 0
which is the first index of obj
, to the last index which is 6
by using the values in each of it and linking its last array value to the next object. If that makes sense.
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 6]
[0, 1, 2, 5, 6]
[0, 1, 3, 4, 5, 6]
[0, 1, 3, 4]
[0, 1, 3, 6]
[0, 3, 4, 5, 6]
[0, 3, 6]
[0, 4]
This is my code so far, as I don't know how to proceed further..
var result = [];
for (var key in obj) {
var myarr = obj[key];
for (var i = 0; i < myarr.length; i++) {
result.push(myarr[i])
}
}
Any idea or feedback is welcome.
Edit
One of the expected result was [0, 1, 2, 3, 4, 5, 6]
, here's the step by step explanation.
obj
key starts from 0
and ends in 6
, I have to form a way from 0
to 6
with the arrays in its value.obj[0]
, the first array returns [0, 1]
, save this to res
. (res
is now [0, 1]
)res
is 1
, now find the next value in obj[1]
obj[1]
has two arrays, and ends with 2
or 3
.. So it's possible to append with both of them, so it can be [0, 1, 2]
or [0, 1, 3]
. In this case, get the first one which is [1, 2]
and append the last value to res
. (res
is now [0, 1, 2]
).res
is now 2
, now find the next value in obj[2]
.obj[2]
has two arrays, and ends with 3
, or 5
.. It's possible to append with both of them, so it can be [0, 1, 2, 3]
or [0, 1, 2, 5]
. In this case, get the first one which is [2, 3]
and append the last value to res
. (res
is now [0, 1, 2, 3]
)res
is now 3
, now find the next value in obj[3]
.res
is now [0, 1, 2, 3, 4]
).res
is now 4
, now find the next value in obj[4]
.res
is now [0, 1, 2, 3, 4, 5]
).res
is now 5
, now find the next value in obj[5]
.6
is found which should be the end of iteration if you look at the step 4
. Repeat step 4 or 6. (res
is now [0, 1, 2, 3, 4, 5, 6]
).[0, 1, 2, 3, 4, 5 ,6]
.Upvotes: 6
Views: 987
Reputation: 42352
Well, I was sitting on this for some time now, and sharing across my take on the problem:
The input object can be considered as an adjacency list of a tree:
var obj={0:[[0,1],[0,3],[0,4]],1:[[1,2],[1,3]],2:[[2,3],[2,5]],3:[[3,4],[3,6]],5:[[5,6]],6:[[6,5]]};
and the following as the result required, which is in fact, as I see it, the list of all root-to-leaf paths of the tree:
[0,1,2,3,4]
[0,1,2,3,6]
[0,1,2,5,6]
[0,1,3,4]
[0,1,3,6]
[0,3,4]
[0,3,6]
[0,4]
a little different than the result set mentioned in the question which is the below:
[0,1,2,3,4,5,6]
[0,1,2,3,6]
[0,1,2,5,6]
[0,1,3,4,5,6]
[0,1,3,4]
[0,1,3,6]
[0,3,4,5,6]
[0,3,6]
[0,4]
The difference between the results is only the question whether 4 and 6 are leaf nodes
Solution:
So I assume that for our Tree here:
0 is the root node
4 and 6 are the leaf nodes
See code below - I created a tree first, and from that listed out all the root to leaf paths:
// removed "6:[[6,5]]" as 6 is a 'leaf' of the tree
var obj={0:[[0,1],[0,3],[0,4]],1:[[1,2],[1,3]],2:[[2,3],[2,5]],3:[[3,4],[3,6]],5:[[5,6]]};
var availableNodes = Object.keys(obj);
var tree = availableNodes.reduce(function(hash) {
return function(prev, curr) {
hash[curr] = hash[curr] || {};
hash[curr].children = hash[curr].children || [];
obj[curr].forEach(function(element) {
hash[element[1]] = hash[element[1]] || {};
hash[element[1]].children = hash[element[1]].children || [];
hash[curr].rootPath = hash[curr].rootPath || [];
hash[curr].children.push({value: element[1],children: hash[element[1]].children});
});
curr && prev.push({value: curr,children: hash[curr].children});
return prev;
};
}(Object.create(null)), []);
//console.log(JSON.stringify(tree));
var result = [];
function rootToLeafPaths(node, path) {
path.push(+node.value);
if (node.children.length === 0) {
result.push(Array.from(path));
path.pop();
} else {
node.children.forEach(function(element) {
rootToLeafPaths(element, path);
});
path.pop();
}
}
rootToLeafPaths(tree[0], []);
console.log(JSON.stringify(result));
.as-console-wrapper{top:0;max-height:100%!important;}
Upvotes: 3
Reputation: 386540
This is a proposal, with a single extra output, mentioned below.
[ [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 6], [0, 1, 2, 5, 6], [0, 1, 3, 4, 5, 6], /* extended from below */ [0, 1, 3, 4], /* original result */ [0, 1, 3, 6], [0, 3, 4, 5, 6], /* extended from below */ [0, 3, 4], /* extra line, line should not be in result */ [0, 3, 6], /* but follows the same building rule than above */ [0, 4] ]
Basically this solution is building a tree with the given information about linked nodes.
If some nodes are not contiguous, a backtracking is made for the missing links, with the above function for nodes, checkNodes
or with iterPath
, to walk the actual collected nodes for missing items.
function getParts(value, path, nodes) {
function checkNodes(a) {
if (a[1] === value + 1) {
getParts(a[1], path.concat(a[1]), nodes);
return true;
}
}
function iterPath(k) {
return (object[k] || []).some(function (a) {
return path[path.length - 1] + 1 === a[1] || iterPath(a[1]);
});
}
value = value || 0;
path = path || [value];
nodes = nodes || [];
if (object[value]) {
object[value].forEach(function (a, i, aa) {
if (a[1] === lastKey) {
parts.push(path.concat(a[1]));
return;
}
getParts(a[1], path.concat(a[1]), nodes.concat(aa.slice(i + 1)));
});
return;
}
if (nodes.some(checkNodes)) {
return;
}
path.slice(1).some(iterPath) && getParts(path[path.length - 1] + 1, path.concat(path[path.length - 1] + 1), nodes);
parts.push(path);
}
var object = {
0: [[0, 1], [0, 3], [0, 4]],
1: [[1, 2], [1, 3]],
2: [[2, 3], [2, 5]],
3: [[3, 4], [3, 6]],
5: [[5, 6]],
6: [[6, 5]]
},
lastKey = 6,
parts = [];
getParts();
parts.forEach(function (a) { console.log(JSON.stringify(a)); });
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 10