This is an assignment. I have the following code:
#! /bin/bash
y=$1
if [ -z $1 ] # if year is not specified use the current year
then y=(`date %Y`)
fi
for m in {1..12}; do
if [ $m -eq 12 ] # december exception
then echo $(date -d $m/1/$y %b) - $(date -d "$(($m 1))/1/$y" %A)
break
fi
echo $(date -d $m/1/$y %b) - $(date -d "$(($m 1))/1/$y - 1 days" %A) # print the last day of the week for the month
done
It lists the last day of the week for every month:
Jan - Monday
Feb - Monday
Mar - Thursday
Apr - Saturday
May - Tuesday
Jun - Thursday
Jul - Sunday
Aug - Wednesday
Sep - Friday
Oct - Monday
Nov - Wednesday
Dec - Saturday
Now I need to reverse it, so that it lists months ending on every day of the week like so:
Sunday - Jul
Monday - Jan Feb Oct
Tuesday - May
Wednesday - Aug Nov
Thursday - Mar Jun
Friday - Sep
Saturday - Apr Dec
I'm thinking of a nested loop,
for d in {1..7};
And storing months in an array?
CodePudding user response:
#! /usr/bin/env bash
# if year is not specified use the current year
declare -r year="${1:-$(date %Y)}"
# associative array (aka hash table)
declare -A months_per_day=()
for m in {01..12}; do
day_month=$(LANG=C date -d "${year}-${m}-01 1 month -1 day" "%A %b")
months_per_day[${day_month% *}] =" ${day_month#* }"
done
for day in Sunday Monday Tuesday Wednesday Thursday Friday Saturday; do
echo "${day} -${months_per_day[${day}]:-}"
done
Output:
Sunday - Jul
Monday - Jan Feb Oct
Tuesday - May
Wednesday - Aug Nov
Thursday - Mar Jun
Friday - Sep
Saturday - Apr Dec
CodePudding user response:
Using jq your task can be solved:
INPUT='
Jan - Monday
Feb - Monday
Mar - Thursday
Apr - Saturday
May - Tuesday
Jun - Thursday
Jul - Sunday
Aug - Wednesday
Sep - Friday
Oct - Monday
Nov - Wednesday
Dec - Saturday
'
jq -Rrs '
split("\n") | # split by lines
map(select(length > 0) | # remove empty lines
split(" - ")) | # split each line by "-"
group_by(.[1]) | # group by weekday
map(.[0][1] " - " (map(.[0]) | join(" ")))[] # generate output
' <<< "$INPUT"
Output
Friday - Sep
Monday - Jan Feb Oct
Saturday - Apr Dec
Sunday - Jul
Thursday - Mar Jun
Tuesday - May
Wednesday - Aug Nov
Remark
The output is sorted alphabetically.
CodePudding user response:
This answer refactors your implementation as a reusable getlastday
function. Then, we go through the loop 7 times matching the getlastday to the matching months and print it out:
#!/bin/bash
getlastday() {
if [ $m -eq 12 ]; then
echo $(date -d "$(($m 1))/1/$y" %A)
return
fi
echo $(date -d "$(($m 1))/1/$y - 1 days" %A) # print the last day of the week for the month
}
y=$1
if [ -z $1 ] # if year is not specified use the current year
then y=(`date %Y`)
fi
for d in Monday Tuesday Wednesday Thursday Friday Saturday Sunday; do
months=()
for m in {1..12}; do
lastday=$(getlastday)
if [ $lastday != $d ]; then continue; fi
months =($(date -d $m/1/$y %b))
done
echo $d - ${months[@]}
done
CodePudding user response:
Try to follow this algorithm to get the expected result:
# Initialize an array to hold the months that end on each day of the week
# Loop over the months from the starting month/year to the ending month/year
# get the last day of the month
# get the day of the week for the last day of the month
# Append the month to the array for the corresponding day of the week
# Loop over the days of the week and print the list of months that end on each day
wish that it will help you
CodePudding user response:
Try this Shellcheck-clean code:
#! /bin/bash -p
year=${1-$(date %Y)}
months=( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec )
day_months=( 'Sunday -' 'Monday -' 'Tuesday -' 'Wednesday -'
'Thursday -' 'Friday -' 'Saturday -' )
for m in {1..12}; do
last_day=$(date -d "$year-$m-1 1 month -1 day" %w)
day_months[last_day] =" ${months[m-1]}"
done
printf '%s\n' "${day_months[@]}"
CodePudding user response:
Using GNU awk for time functions, with lots of intermediate and descriptively named variables to make it easy to understand:
$ cat tst.sh
#!/usr/bin/env bash
awk -v year="$1" '
BEGIN {
OFS = " - "
year = (year == "" ? strftime("%Y") : year)
secsInDay = 24*60*60
for ( mthNr=1; mthNr<=12; mthNr ) {
lastDayEpochSecs = mktime(year " " (mthNr 1) " 1 12 0 0") - secsInDay
mthAbbrDayName = strftime("%b %A", lastDayEpochSecs)
split(mthAbbrDayName,m)
mthAbbr = m[1]
dayName = m[2]
mthNr2mthAbbr[mthNr] = mthAbbr
mthAbbr2dayName[mthAbbr] = dayName
dayName2mthAbbrs[dayName] = \
(dayName in dayName2mthAbbrs ? dayName2mthAbbrs[dayName] " " : "" ) mthAbbr
}
for ( mthNr=1; mthNr<=12; mthNr ) {
mthAbbr = mthNr2mthAbbr[mthNr]
dayName = mthAbbr2dayName[mthAbbr]
print mthAbbr, dayName
}
print "\n--------\n"
for ( dayName in dayName2mthAbbrs ) {
mthAbbrs = dayName2mthAbbrs[dayName]
print dayName, mthAbbrs
}
}
'
$ ./tst.sh
Jan - Monday
Feb - Monday
Mar - Thursday
Apr - Saturday
May - Tuesday
Jun - Thursday
Jul - Sunday
Aug - Wednesday
Sep - Friday
Oct - Monday
Nov - Wednesday
Dec - Saturday
--------
Tuesday - May
Friday - Sep
Sunday - Jul
Thursday - Mar Jun
Saturday - Apr Dec
Monday - Jan Feb Oct
Wednesday - Aug Nov
The above will be much faster than calling date
multiple times in a shell loop and is trivial to modify to do anything else you need.