BradStevenson
BradStevenson

Reputation: 1994

Converting months between numeric and number using awk

In a past paper for an exam I have the question:

Months can be represented in different ways, for example as numbers (1, 2, …, 12), or as three- letter month names (Jan, Feb, …, Dec). Suggest how associative arrays in awk can be used to translate from three-letter month names to month numbers, and vice versa, to translate month numbers to three-letter month names.

So I thought I would use associative arrays in the format say the input of the month is in $1:

number_to_month["Jan"] = 1;
print number_to_month[$1]

But to me this doesn't seem to leverage the power of associative arrays very well, plus I have to initialise each month in the array manually.

What are my other options?

Upvotes: 4

Views: 4121

Answers (5)

glenn jackman
glenn jackman

Reputation: 247042

Here's another take, using a couple of helper functions:

awk '
    BEGIN { 
        j = 0
        for (i=1; i<=34; i+=3) {
            months[substr("JanFebMarAprMayJunJulAugSepOctNovDec",i,3)] = ++j
        }
    }
    function month2num(month) {
        return (month in months ? months[month] : -1)
    }
    function num2month(n) {
        for (month in months) {
            if (months[month] == n)
                return month
        }
        return ""
    }
    BEGIN {
        print "Jan: " month2num("Jan")
        print "Dec: " month2num("Dec")
        print "Foo: " month2num("Foo")
        print "3: " num2month(3)
        print "12: " num2month(12)
        print "14: " num2month(14)
    }
'

Upvotes: 0

Ed Morton
Ed Morton

Reputation: 204229

$ cat tst.awk
BEGIN {
   mths="JanFebMarAprMayJunJulAugSepOctNovDec"

   name="Mar"; print name " -> " (match(mths,name)+2)/3
   name="Sep"; print name " -> " (match(mths,name)+2)/3

   nbr=3;      print nbr  " -> " substr(mths,(nbr*3)-2,3)
   nbr=9;      print nbr  " -> " substr(mths,(nbr*3)-2,3)
}

$ awk -f tst.awk
Mar -> 3
Sep -> 9
3 -> Mar
9 -> Sep

and here's @dmckee's script modified to produce ordered output:

$ cat tst2.awk
BEGIN {
    n = split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec",month)
    for (i in month) {
        month_nums[month[i]]=i
    }

    for (i=1; i<=n; i++) {
        print i "\t" month[i]
    }
    for (i=1; i<=n; i++) {
        m = month[i]
        print m "\t" month_nums[m]
    }
}
$ awk -f tst2.awk
1       Jan
2       Feb
3       Mar
4       Apr
5       May
6       Jun
7       Jul
8       Aug
9       Sep
10      Oct
11      Nov
12      Dec
Jan     1
Feb     2
Mar     3
Apr     4
May     5
Jun     6
Jul     7
Aug     8
Sep     9
Oct     10
Nov     11
Dec     12

Upvotes: 1

Guru
Guru

Reputation: 17014

In case you do not want to initialize array manually, one option is:

echo | awk '{x=mktime("2013 01 01 0 0 0"); for(i=0;i<12;i++){s=strftime("%b",x+((31*i)*86400)); m[s]=i+1;n[i+1]=s;}}'

This will create 2 arrays, m & n, where m is the array in which index is the month name and value is month number, and the n array is vice-versa.

Upvotes: 2

The builtin split function is your friend here, and looping can copy the name-from-number version into the number-from-name:

BEGIN {
    split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec",month)
    for (i in month) {
        month_nums[month[i]]=i
    }
}
END {
    for (i in month) {
        print i "\t" month[i]
    }
    for (m in month_nums) {
        print m "\t" month_nums[m]
    }
}

The BEGIN block shows how to do it. Then END block just lets you verify it.

The output I get (using gawk 4.0.1) is:

4       Apr
5       May
6       Jun
7       Jul
8       Aug
9       Sep
10      Oct
11      Nov
12      Dec
1       Jan
2       Feb
3       Mar
Feb     2
Sep     9
Jan     1
May     5
Apr     4
Oct     10
Dec     12
Nov     11
Jul     7
Mar     3
Aug     8
Jun     6

notice the usual awkwardness (Heh! AWKwardness) arrising from the inability to force the access order on array for loops.

Upvotes: 7

Satish
Satish

Reputation: 721

When they mention awk, I presume this details of months and integer mapping is maintained in data file like

1 JAN JANUARY jan
2 FEB FEBRUARY feb
...

and so on

you can use awk

awk '/JAN/ {print $1}' temp.txt

Upvotes: 0

Related Questions