0

I've a following data in file which has user and supervisor relationship.

user |supervisor |id
-----|-----------|----
a    |   b       | 1
b    |   c       | 2
c    |   d       | 3
e    |   b       | 4

I want to explode the relationship hierarchy between the user and supervisor as below.

user |supervisor |id
-----|-----------|----
a    |   b       | 1
a    |   c       | 1
a    |   d       | 1
b    |   c       | 2
b    |   d       | 2  
c    |   d       | 3
e    |   b       | 4
e    |   c       | 4
e    |   d       | 4 

As you see, for the user 'a', the immediate supervisor is 'b' but again 'b' has 'c' as its supervisor. So indirectly 'c' is supervisor for 'a' as well and so on. Such as, my aim is to explode the hierarchy at any level for a given user. What is the best way to implement this in Unix ?

0

I’m assuming that each user appears in the input file (in the “User” column) only once.  I’m further assuming that the vertical bar (|) separators are actually in the file, and that they are always separated from the data by whitespace, and that the header line is not actually present.

Here’s a two-pass solution using awk.  The first pass builds an array containing everybody’s supervisor; the second pass builds the output:

awk 'pass==1 { super[$1] = $3; }
     pass==2 {
                print
                user=$3
                while (super[user] != "") {
                        print $1, "|", super[user], "|", $5
                        user=super[user]
                }
             }
    ' pass=1 data pass=2 data

This will produce output that’s not properly aligned.  To fix that, pipe it through column -t.  Or we can format the output from within the awk script; if you want that, specify the formatting rules you want.

By the way, this operation is generally known as transitive closure.

0

Complex awk solution:

awk 'NR<3{ h=(h=="")? $0 : h ORS $0 }NR>2{ uid[$1]=$5; us[$1]=$3 }
     END{ 
         print h; 
         for (u in uid) { 
             id=uid[u]; spvr=us[u]; printf("%-5s|%-11s|%-4s\n",u,spvr,id); 
             while (spvr in uid) { 
                 spvr=us[spvr]; printf("%-5s|%-11s|%-4s\n",u,spvr,id) 
             } 
         }
     }' yourfile

The output:

user |supervisor |id
-----|-----------|----
a    |b          |1   
a    |c          |1   
a    |d          |1   
b    |c          |2   
b    |d          |2   
c    |d          |3   
e    |b          |4   
e    |c          |4   
e    |d          |4 

Details:

  • NR<3{ h=(h=="")? $0 : h ORS $0 } - capturing header lines

  • uid[$1]=$5 - user-id relation array

  • us[$1]=$3 - user-supervisor relation array

  • spvr=us[u] - the 1st supervisor for the current user

  • while (spvr in uid) { ... } - while supervisor is in user list, get parent supervisor

0

My awk solution (using output formats from RomanPerekhrest). Basically, there are two relevant loops. First, if a new supervisor is processed for a user, then all dependencies of that supervisor(i.e. supervisor-chains) must be added to the user. After that, a second loop looks for all other users, which have the currently processed user as a dependency and all dependencies of the current user are added to them.:

#!/usr/bin/awk
# file process_it.awk
BEGIN {

    FS="|";
}

NR<3 {

    h=(h==""? $0 : h ORS $0)
}

NR>2 {

   gsub(/ /, "", $0)
   curr_user=$1;
   curr_supervisor=$2;
   curr_id=$3;
   print curr_user, curr_supervisor;
   arr[curr_user][curr_supervisor]++;
   id[curr_user]=curr_id;

   if(isarray(arr[curr_supervisor])) {
       for(sub_indx in arr[curr_supervisor])
           arr[curr_user][sub_indx]++;
    }
    else
        delete arr[curr_supervisor];

    for(indx in arr) {

        if(isarray(arr[indx])) {

            for(sub_indx in arr[indx]) {

                if(sub_indx==curr_user) {

                    for(sub_indx2 in arr[curr_user])
                    arr[indx][sub_indx2]++;
                }
            }    
        }
    }
}

END {
    print h;

    for(i in arr) {

        if(isarray(arr[i])) {

            for(j in arr[i])
                printf "%-5s|%-11s|%-3s\n", i, j, id[i];
        }
    }
}

Use:

awk -f process_it.awk your_file.txt

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.