5

I've a CSV file containing records like below.

   id,h1,h2,h3,h4,h5,h6,h7 
   101,zebra,1,papa,4,dog,3,apple
   102,2,yahoo,5,kangaroo,7,ape

I want to sort rows into this file without header and first column. My output should like this.

  id,h1,h2,h3,h4,h5,h6,h7
  101,1,3,4,apple,dog,papa,zebra
  102,2,5,7,ape,kangaroo,yahoo

I tried below AWK but don't know how to exclude header and first column.

awk -F"," ' {
s=""
for(i=1; i<=NF; i++) { a[i]=$i; }
for(i=1; i<=NF; i++)
{
for(j = i+1; j<=NF; j++)
{
if (a[i] >= a[j])
{
temp = a[j];
a[j] = a[i];
a[i] = temp;
}
}
}
for(i=1; i<=NF; i++){ s = s","a[i]; }
print s
} 
' file

Thanks

Priyanka
  • 169
  • 10

3 Answers3

9

If perl is okay:

$ perl -F, -lane 'print join ",", $.==1 ? @F : ($F[0], sort @F[1..$#F])' ip.txt
id,h1,h2,h3,h4,h5,h6,h7 
101,1,3,4,apple,dog,papa,zebra
102,2,5,7,ape,kangaroo,yahoo
  • -F, to indicate , as input field separator, results saved in @F array
  • join "," to use , as output field separator
  • $.==1 ? @F for first line, print as is
  • ($F[0], sort @F[1..$#F]) for other lines, get first field and sorted output of other fields
    • .. is range operator, $#F will give index of last field
    • you can also use (shift @F, sort @F) instead of ($F[0], sort @F[1..$#F])

For given header, sorting first line would work too, so this can simplify logic required

$ # can also use: perl -F, -lane 'print join ",", shift @F, sort @F'
$ perl -F, -lane 'print join ",", $F[0], sort @F[1..$#F]' ip.txt
id,h1,h2,h3,h4,h5,h6,h7 
101,1,3,4,apple,dog,papa,zebra
102,2,5,7,ape,kangaroo,yahoo

$ # can also use: ruby -F, -lane 'print [$F.shift, $F.sort] * ","'
$ ruby -F, -lane 'print [$F[0], $F.drop(1).sort] * ","' ip.txt
id,h1,h2,h3,h4,h5,h6,h7 
101,1,3,4,apple,dog,papa,zebra
102,2,5,7,ape,kangaroo,yahoo
Sundeep
  • 23,246
  • 2
  • 28
  • 103
  • 2
    `perl` rocks!! you too! – P.... Feb 22 '18 at 06:46
  • 1
    I just came here from [your 2nd perl one-liners article](https://www.perl.com/article/perl-one-liners-part-2/) to say that you can do natural sorts with the [Sort::Versions](https://metacpan.org/release/Sort-Versions) or [Sort::Naturally](https://metacpan.org/release/Sort-Naturally) modules if you have one of them installed. e.g. `perl -MSort::Naturally -F, -lane 'print join ",", $.==1 ? @F : (shift @F, nsort @F)' ip.txt` – cas Jun 09 '21 at 12:12
  • @cas thanks, I recently used [version](https://metacpan.org/pod/version), will check out `Sort::Naturally` as well. – Sundeep Jun 09 '21 at 12:56
5

if you have gawk use asort:

awk -v OFS="," 'NR>1{split($0, a, ",");
                $1=a[1];
                delete a[1];
                n = asort(a, b);
                for (i = 1; i <= n; i++){ $(i+1)=b[i]}};
                1' file.csv

This splits the columns to array a with seperator as , for all raws except the first one.

Then assign the first value in the column in a raw with the first value in a and delete this value from a.

Now the a is sorted to b and assign value starting from 2 column. then print it.

jijinp
  • 2,592
  • 1
  • 13
  • 15
2

You can just use the asort() function in awk for your requirement and start sorting them from second line on-wards. The solution is GNU awk specific because of length(array) function

awk 'NR==1{ print; next }
     NR>1 { finalStr="" 
            arrayLength=""
            delete b
            split( $0, a, "," )
            for( i = 2; i <= length(a); i++ )
                b[arrayLength++] = a[i]
            asort( b )
            for( i = 1; i <= arrayLength ; i++ ) 
                finalStr = (finalStr)?(finalStr","b[i]):(b[i])
            printf( "%s", a[1]","finalStr )
            printf( "\n" ); 
    }' file

The idea is first we split the entire line with a , delimiter into the array a from which we get the elements from the 2nd field onwards to a new array b. We sort those elements in this new array and append the first column element when we print it finally.

Inian
  • 80,270
  • 14
  • 142
  • 161