Given an table of links of the form:
const links = [
['a', 'b'],
['c', 'd'],
['e', 'f'],
['b', 'x'],
['x', 'z']
];
where the first column is unique i.e. links[0...length][0]
are all unique.
I would like to find all connections greater than 1. In the example above, the output should be
[["a", "b"], ["b", "x"], ["x", "z"]]
Here is my attempt which is based on a similar question for java
const links = [
['a', 'b'],
['c', 'd'],
['e', 'f'],
['b', 'x'],
['x', 'z']
];
connections = [];
const map = new Map()
const recurse = (value, key) => {
if (map.has(value)) {
if (key !== undefined) {
connections.push([key, map.get(key)]);
}
connections.push([value, map.get(value)])
recurse(map.get(value))
}
}
const findConnectionsWithMap = arr => {
arr.forEach(value => map.set(value[0], value[1]))
const keySet = map.keys();
//console.log(keySet);
while (key = keySet.next().value) {
recurse(map.get(key), key);
}
console.log(connections);
}
findConnectionsWithMap(links);
Current output is
[["a", "b"], ["b", "x"], ["x", "z"], ["b", "x"], ["x", "z"]]
I am not sure why the output is having duplicates but I am guessing it has to do with the recursion point.
Why is this happening? and is this the best approach for a large record set?
CodePudding user response:
Based on my recent answer of detecting cyclical paths in array (much like this one), here's the solution.
This one is without recursion, just looping on all items, finding chains. this assumes only 1 -1 links. If that is not the case do let know in the comments.
const links = [
['a', 'b'],
['c', 'd'],
['e', 'f'],
['b', 'x'],
['x', 'z']
];
function search_id(arr, id) {
return arr.find(item => item[0] === id)
}
function find_paths(arr) {
var result = []
for (var i = 0; i < arr.length; i ) {
var item = arr[i];
var seen = [item[0]]
while (true) {
var target = search_id(arr, item[1]);
if (target === undefined) {
break;
}
if (seen.indexOf(target[0]) > -1) {
// cyclical
console.error("cyclical chain found ")
break;
}
seen.push(target[0]);
item = target;
}
if (seen.length > 1) {
result.push(seen)
}
}
return result;
}
console.log(find_paths(links));
CodePudding user response:
You could use recursion and call the recursive method as long as the link is found between two elements. To keep chains unique you also have to remove the element once the element belongs to some chain otherwise for your data example you will actually have two chains [["a","b"],["b","x"],["x","z"]]
and [["b","x"],["x","z"]]
const links = [
['a', 'b'],
['c', 'd'],
['e', 'f'],
['b', 'x'],
['x', 'z']
];
function getConnections(data) {
function connect(a, data) {
const chain = []
const index = data.findIndex((b) => a[1] === b[0])
if (index > -1) {
chain.push(data[index], ...connect(data[index], data))
data.splice(index, 1)
}
return chain
}
return data.reduce((r, e) => {
const chain = connect(e, data)
if (chain.length) {
r.push([e, ...chain])
}
return r
}, [])
}
console.log(JSON.stringify(getConnections(links)))