Given an array with two entries, parent and child relation, convert the array to a relation tree object (parent -> child -> grandchild) in JavaScript.
The input array will contain relations for many ancestries in random order, We must return the array of strings representing different relationships.
For example, in the below case, the topmost ancestor is an animal.
Example
Input: [ ["lion", "cat"], ["cat", "mammal"], ["dog", "mammal"], ["mammal", "animal"], ["fish", "animal"], ["shark", "fish"], ]; Output: [ "animal -> mammal -> cat -> lion", "animal -> mammal -> cat", "animal -> mammal -> dog", "animal -> mammal", "animal -> fish", "animal -> fish -> shark" ]
Convert relation array to object tree in JavaScript.
The first thing we have to do is to convert the array to an object of the parent-child relationship for better processing.
// aggregate parent / child relation const aggregate = (arr) => { // aggregate the values for easier processing return arr.reduce((a, b) => { const [child, parent] = b; // aggregating on child a[child] = parent; return a; }, {}); } const arr = [ ["lion", "cat"], ["cat", "mammal"], ["dog", "mammal"], ["mammal", "animal"], ["fish", "animal"], ["shark", "fish"], ]; console.log(aggregate(arr)); /* { "lion": "cat", "cat": "mammal", "dog": "mammal", "mammal": "animal", "fish": "animal", "shark": "fish" } */
We have aggregated the values on the child as one child will have only one parent, this way a single key-value pair is formed which is faster in forming relationships.
Now, all we have to do is recursively keep on traversing this map and form the relation string. For this, we will get the value of the current key and recursively call the same function with this value and the map to get its ancestry and so on.
// for a relationship from the aggregated value const convert = (obj) => { return Object.keys(obj).reduce((a, b) => { a.push(getKey(obj, b)); return a; }, []); } // helper function to form the string // till the last hierarchy const getKey = (obj, key) => { // access the const val = obj[key]; // the formation can be reversed by chaning the order of the keys // child -> parent | parent -> child if(val in obj){ return getKey(obj, val) + " -> " + key; }else{ return val + " -> " + key; } }
Input: // map after aggregation const map = { "lion": "cat", "cat": "mammal", "dog": "mammal", "mammal": "animal", "fish": "animal", "shark": "fish" }; console.log(convert(map)); Output: [ "animal -> mammal -> cat -> lion", "animal -> mammal -> cat", "animal -> mammal -> dog", "animal -> mammal", "animal -> fish", "animal -> fish -> shark" ]
Complete code
const ancestry = (arr) => { // aggregate parent / child relation const aggregate = (arr) => { // aggregate the values for easier processing return arr.reduce((a, b) => { const [child, parent] = b; // aggregating on child a[child] = parent; return a; }, {}); }; // for a relationship from the aggregated value const convert = (obj) => { return Object.keys(obj).reduce((a, b) => { a.push(getKey(obj, b)); return a; }, []); }; // helper function to form the string // till the last hierarchy const getKey = (obj, key) => { // access the const val = obj[key]; // the formation can be reversed by chaning the order of the keys // child -> parent | parent -> child if(val in obj){ return getKey(obj, val) + " -> " + key; }else{ return val + " -> " + key; } }; // get the aggregated map const aggregatedMap = aggregate(arr); // return the ancestory return convert(aggregatedMap); };
Input: const arr = [ ["lion", "cat"], ["cat", "mammal"], ["dog", "mammal"], ["mammal", "animal"], ["fish", "animal"], ["shark", "fish"], ]; console.log(ancestry(arr)); Output: [ "animal -> mammal -> cat -> lion", "animal -> mammal -> cat", "animal -> mammal -> dog", "animal -> mammal", "animal -> fish", "animal -> fish -> shark" ]