How to convert the row to column in Perl?

Dear Perl users,

Could you help me how to convert from row to column if I've a case below:

Linux           2014_01_24           CPU            10
Linux          2014_01_24            MEM        20
UNIX          2014_01_24           CPU            30
UNIX         2014_01_24            MEM        40 

and the desired output will be :

NAME      TIME                Linux           UNIX
CPU       2014_01_24         10                 30         
MEM     2014_01_24          20                40

Thank you very much

You can try this with Shell like below :_

#!/bin/sh
awk 'BEGIN{print "NAME\tTIME\tLinux\tUNIX"}' > output.txt
grep -i mem test | paste -s - | awk '{print $3"\t"$2"\t"$4"\t"$8}' >>output.txt
grep -i CPU test | paste -s - | awk '{print $3"\t"$2"\t"$4"\t"$8}' >>output.txt

Here is your desired output:-

cat output.txt


 
NAME    TIME    Linux   UNIX
MEM     2014_01_24      20      40
CPU     2014_01_24      10      30
import itertools
import operator
a='''Linux 2014_01_24 CPU 10
Linux 2014_01_24 MEM 20
UNIX 2014_01_24 CPU 30
UNIX 2014_01_24 MEM 40'''.split("\n")
a=map(lambda x:x.split(" "),a)
for name,item in itertools.groupby(sorted(a,key=operator.itemgetter(2,1)),operator.itemgetter(2,1)):
    print(name[0],name[1],end=" ")
    for i in item:
        print(i[-1],end=" ")
    print("")
perl -lane 'BEGIN {print "NAME TIME Linux UNIX"};
if ($F[0] eq "Linux") {
    ${ $x{"$F[2] $F[1]"} }[0] = $F[3];
}
elsif ($F[0] eq "UNIX") {
    ${ $x{"$F[2] $F[1]"} }[1] = $F[3];
}
END { print "$_ ${ $x{$_} }[0] ${ $x{$_} }[1]" for (keys %x) }' file

Hi Belajesuri,
Thanks alot for your Perl code and to the others also I would like to say thanks so much but I need a Perl script.

I try to reformat your code from one-liners to the common way of Perl language and it works.

My next question is how I can set the header "Linux" and "UNIX" as variables instead of static values?. so when the other line comes, the header will be added also as well as the value of CPU anf MEM (e.g. )?.

Linux           2014_01_24            CPU            10
Linux           2014_01_24            MEM            20
UNIX            2014_01_24            CPU            30
UNIX            2014_01_24            MEM            40
WINDOWS         2014_01_24            CPU            70
WINDOWS         2014_01_24            MEM            80

so the result will be = NAME TIME Linux UNIX WINDOWS.. so on

#!/usr/bin/perl
use strict;

my %table;

print "NAME TIME  Linux UNIX\n";

foreach (<FH>)
{  chomp;
   my @temp = split " ", $_;
   
   if ($temp[0] eq "Linux")
   {
       $table{"$temp[2] $temp[1]"}[0] = $temp[3];   
   }
   elsif($temp[0] eq "UNIX")
   {
       $table{"$temp[2] $temp[1]"}[1] = $temp[3];   
   }
    
}

print "$_ ${$table{$_}}[0] ${ $table{$_} }[1]" for (keys %table);

---------- Post updated at 10:19 PM ---------- Previous update was at 04:17 PM ----------

Hi Balajesuri,

I've made the script based on your original code and the desired output already oke. But I think my script is still ugly. may be you have a good solution for this?.

#!/usr/bin/perl
use strict;
use Data::Dumper;

my %table;

my $file = "convert_column.log";

open (FH, "<", $file) or die $!;

my @node_name;

foreach my $line (<FH>)
{
  chomp;
  my @temp = split " ", $line;
  push @node_name, $temp[0];
}

my %seen;
my @node_name_without_duplicate;

for (@node_name)
{
  $seen{$_}++;
  next if $seen{$_}++>1;
  push @node_name_without_duplicate, $_;
}


close (FH);

my $length_node = scalar @node_name_without_duplicate;
print "NAME TIME  @node_name_without_duplicate\n";
open (FH, "<", $file) or die $!;

my %table;  

foreach (<FH>)
{
    for my $i (0 .. $length_node-1)
    {    
      my @temp = split " ", $_;
      if ($temp[0] eq $node_name_without_duplicate[$i])
      { 
        $table{"$temp[2] $temp[1]"}[$i] = $temp[3];
      }
    }    
}
close (FH);
   
for (keys %table)
{
    
        print "$_ ";
    for my $i (0 .. $length_node-1)
    {
      print " $table{$_}->[$i]";
        }

    print "\n";
}

You are reading the file twice - this should be avoided, unless absolutely necessary.

#! /usr/bin/perl
use warnings;
use strict;

my ($line, $k1, $k2);
my (@fld, @temp);
my (%os, %final);

open I, "< file";
while ( $line = <I> ) {
    chomp ( $line );
    @fld = split /\s+/, $line;
    $os{$fld[0]} = 1;
    ${ $final{"$fld[2] $fld[1]"} }{$fld[0]} = $fld[3];
}
close I;

my @OS_list = sort ( keys %os );
print "NAME TIME @OS_list\n";

for $k1 ( keys %final ) {
    print "$k1 ";
    for $k2 ( @OS_list ) {
        if ( defined ${ $final{$k1} }{$k2} ) {
            print "${ $final{$k1} }{$k2} ";
        }
        else {
            print "--- ";
        }
    }
    print "\n";
}
[user@host ~]$ cat file
Linux           2014_01_24            CPU            10
Linux           2014_01_24            MEM            20
UNIX            2014_01_24            CPU            30
UNIX            2014_01_24            MEM            40
WINDOWS         2014_01_24            CPU            70
WINDOWS         2014_01_24            MEM            80
HPUX            2014_01_22            CPU            90
HPUX            2014_01_22            MEM            100
[user@host ~]$ ./test.pl
NAME TIME HPUX Linux UNIX WINDOWS
MEM 2014_01_22 100 --- --- ---
MEM 2014_01_24 --- 20 40 80
CPU 2014_01_24 --- 10 30 70
CPU 2014_01_22 90 --- --- ---
[user@host ~]$

I know you want a perl script but here a solution in awk just for the fun of it

awk '
  {
    if(!($1 in os)) { os[$1]=++i; ios=$1 }
    com[$3]; date[$2]
    data[$3, os[$1], $2]=$4
  }
  END{
    printf("%-11s%-11s", "NAME", "TIME")
    for(a=1; a<=i; a++) printf("%-11s", ios[a])
    printf "\n"
    for(b in com){
      for(c in date){
        p=0; str=""
        for(j=1; j<=i; j++){
          if(data[b, j, c]) p=1
          str=str sprintf("%-11s", data[b, j, c]?data[b, j, c]:"---")
        }
        if(!p) continue
        printf("%-11s%-11s%s\n", b, c, str)
      }
    }
  } ' infile
root@maximus-netpune-21:/tmp# ./run
NAME       TIME       Linux      UNIX       WINDOWS    HPUX
CPU        2014_01_22 ---        ---        ---        90
CPU        2014_01_24 10         30         70         ---
MEM        2014_01_22 ---        ---        ---        100
MEM        2014_01_24 20         40         80         ---

--ahamed